-
-
-**Additional environment details:**
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index 62365b191..000000000
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,18 +0,0 @@
-
diff --git a/.github/actions/lima-vm-setup/action.yml b/.github/actions/lima-vm-setup/action.yml
deleted file mode 100644
index 28c0d2047..000000000
--- a/.github/actions/lima-vm-setup/action.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: 'Lima VM Setup'
-description: 'Install Lima, enable KVM, start a VM and copy the CRIU source into it'
-
-inputs:
- template:
- description: 'Lima VM template name (e.g. fedora, centos-stream-9)'
- required: true
- cache-key-prefix:
- description: 'Prefix for the Lima image cache key'
- required: true
-
-runs:
- using: composite
- steps:
- - name: Install Lima
- uses: lima-vm/lima-actions/setup@55627e31b78637bf254a8b2a14da8ea7d12564e5 # v1
- - name: Cache Lima images
- uses: actions/cache@v6
- with:
- path: ~/.cache/lima
- key: ${{ inputs.cache-key-prefix }}-${{ github.sha }}
- restore-keys: ${{ inputs.cache-key-prefix }}-
- - name: Start VM
- shell: bash
- # Enable VNC display so QEMU attaches a VGA device instead of
- # passing -vga none. Without it the kernel only registers a CGA
- # text console and VT ioctls (TIOCSLCKTRMIOS, TIOCSWINSZ) fail,
- # which breaks the zdtm/static/vt test on restore.
- run: limactl start --plain --name=default --cpus=4 --memory=12 --set '.video.display = "vnc"' template://${{ inputs.template }}
- - name: Copy source into VM
- shell: bash
- run: |
- lima sudo mkdir -p /home/criu
- lima sudo chown "$(lima whoami)" /home/criu
- limactl copy -r . default:/home/criu
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
deleted file mode 100644
index e7dac6a87..000000000
--- a/.github/copilot-instructions.md
+++ /dev/null
@@ -1,95 +0,0 @@
-# GitHub Copilot Instructions for CRIU
-
-CRIU (Checkpoint/Restore In User-space) is a specialized tool for checkpointing
-and restoring running processes on Linux.
-
-## Coding Style & Conventions
-All C code MUST follow the [Linux Kernel Coding Style](https://www.kernel.org/doc/html/latest/process/coding-style.html).
-
-- **Indentation**: Use hard tabs. Set tab width to 8 characters.
-- **Line Length**: Preferred limit is 80 characters. Max 120 if it
- significantly improves readability.
-- **Braces**:
- - Functions: Opening brace on a new line.
- - Blocks (`if`, `for`, `while`, `switch`): Opening brace on the same line as
- the statement.
-- **Spaces**: Use spaces around operators (`+`, `-`, `*`, `/`, `%`, `<`, `>`,
- `=`, etc.).
-- **Naming**: Use descriptive, snake_case names for functions and variables.
-- **Comments**: Use C-style comments (`/* ... */`).
- - Multi-line format:
- ```c
- /*
- * This is a multi-line
- * comment.
- */
- ```
-
-## Architecture Overview
-- **criu/**: Contains the main logic for checkpoint and restore.
-- **compel/**: Sub-project for "parasite" code injection and PIE blob
- generation.
-- **images/**: Protobuf descriptions for image files. Use these to understand
- the state being saved.
-- **restorer**: PIE code that handles the final stages of process restoration.
- See `criu/include/restorer.h` for `CR_STATE_*` definitions.
-- **crit**: Tooling for inspecting CRIU image files.
-- **soccr**: Library for TCP socket checkpoint/restore.
-- **pie/ directories**: Code in these directories (e.g., `criu/pie/`) should be
- self-contained Position-Independent Executable (PIE) code. It MUST NOT
- depend on any external libraries and can only depend on things implemented by
- Compel.
-
-### CRIU Commands
-- **dump**: Saves a process tree and all its related resources into a
- collection of image files.
-- **restore**: Restores processes from image files to the same state they were
- in before the dump.
-- **check**: Checks whether the kernel supports the features needed by CRIU to
- dump and restore a process tree.
-- **pre-dump**: Performs the pre-dump procedure, creating a snapshot of memory
- changes since the previous dump/pre-dump (incremental checkpointing).
-- **service**: Launches CRIU in RPC daemon mode, listening for commands over a
- socket.
-- **dedup**: Starts pagemap data deduplication, minimizing image size by
- obtaining references from parent images.
-- **page-server**: Launches CRIU in page server mode to send memory pages over
- the network during migration.
-
-## Development & Testing
-- **ZDTM (Zero-Downtime Migration)**: The primary test suite located in
- `test/zdtm`.
-- **Test Scope**: Each test case targets a specific kernel primitive type
- (e.g., file descriptors, sockets, timers).
-- **Test Purpose**: Verifies that the targeted kernel primitive is
- Checkpointed/Restored (C/R-ed) correctly.
-- **Test Executor**: `test/zdtm.py`.
-- **Running a test**: `sudo ./test/zdtm.py run -t zdtm/static/env00`.
-- **Test Structure**: Tests typically use `test_daemon()` to signal readiness
- and `test_waitsig()` to wait for the C/R cycle to complete. After being
- restored, the test checks that all its resources are still in a valid state.
-
-## Commit Message Guidelines
-Follow these principles when forming commits:
-
-- **Separate each logical change into a separate patch**: Each commit must
- represent a single logical change. Separate bug fixes from performance
- improvements or API updates.
-- **The commit subject has to start with the sub-system prefix**: Prefix the
- subject with the affected component (e.g., `criu:`, `compel:`, `images:`,
- `test:`, or specific file names like `criu-ns:`).
-- **Imperative Mood**: Use the imperative mood in the subject (e.g., "make
- xyzzy do frotz" instead of "changed xyzzy").
-- **Detailed Body**: Explain the problem being solved (the "why") and the
- technical details of the implementation (the "how").
-- **Hard Wrap**: The commit message has to be hard wrapped at 72 characters.
-- **Signed-off-by**: Every commit MUST be signed off (`git commit -s`). This
- certifies the Developer's Certificate of Origin (DCO).
-- **Fixes Tag**:
- - For bugs: `Fixes: <12-char-commit-id> ("summary")`. The `` has
- to be the first 12 characters of the commit SHA-1 ID.
- - For GitHub issues: `Fixes: #`
-- **Atomicity**: Ensure CRIU builds and tests pass after *every* commit in a
- series to maintain bisectability.
-- **No Fixups**: Squash "fixup!" or "work in progress" commits before final
- submission.
diff --git a/.github/workflows/check-commits.yml b/.github/workflows/check-commits.yml
deleted file mode 100644
index 30c155321..000000000
--- a/.github/workflows/check-commits.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: Verify self-contained commits
-
-on: pull_request
-
-# Cancel any preceding run on the pull request
-concurrency:
- group: commit-test-${{ github.event.pull_request.number }}
-
-jobs:
- build:
- runs-on: ubuntu-latest
- # Check if pull request does not have label "not-selfcontained-ok"
- if: "!contains(github.event.pull_request.labels.*.name, 'not-selfcontained-ok')"
- steps:
- - uses: actions/checkout@v7
- with:
- # Needed to rebase against the base branch
- fetch-depth: 0
- # Checkout pull request HEAD commit instead of merge commit
- ref: ${{ github.event.pull_request.head.sha }}
- - name: Install dependencies
- run: sudo contrib/apt-install libprotobuf-dev libprotobuf-c-dev protobuf-c-compiler protobuf-compiler python3-protobuf libnl-3-dev libnet-dev libcap-dev uuid-dev liblz4-dev
- - name: Configure git user details
- run: |
- git config --global user.email "checkpoint-restore@users.noreply.github.com"
- git config --global user.name "checkpoint-restore"
- - name: Configure base branch without switching current branch
- run: git fetch origin ${{ github.base_ref }}:${{ github.base_ref }}
- - name: Build each commit
- run: git rebase ${{ github.base_ref }} -x "make -C scripts/ci check-commit"
- - name: Build without LZ4
- run: |
- make mrproper
- make -j "$(nproc)" NO_LZ4=1
- make unittest NO_LZ4=1
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 7b5533bba..000000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,367 +0,0 @@
-name: CI
-
-on: [push, pull_request]
-
-# Cancel any preceding run on the pull request.
-concurrency:
- group: ci-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: ${{ github.ref != 'refs/heads/criu-dev' }}
-
-jobs:
- alpine-test:
- name: Alpine Test (${{ matrix.target }}, ${{ matrix.shard_name }})
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu-22.04]
- target: [GCC=1, CLANG=1]
- shard: [0, 1, 2, 3, 4]
- include:
- - shard: 0
- shard_name: zdtm 1/4
- - shard: 1
- shard_name: zdtm 2/4
- - shard: 2
- shard_name: zdtm 3/4
- - shard: 3
- shard_name: zdtm 4/4
- - shard: 4
- shard_name: non-zdtm
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v7
- - name: Run Alpine ${{ matrix.target }} ${{ matrix.shard_name }} Test
- run: >
- sudo -E make -C scripts/ci alpine ${{ matrix.target }}
- ZDTM_SHARD_INDEX=${{ matrix.shard }}
- ZDTM_SHARD_COUNT=4
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- alpine-test-arm64:
- name: Alpine Test ARM64
- needs: [alpine-test]
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu-22.04-arm]
- target: [GCC=1, CLANG=1]
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v7
- - name: Run Alpine ${{ matrix.target }} Test
- run: sudo -E make -C scripts/ci alpine ${{ matrix.target }}
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- aarch64-test:
- needs: [alpine-test]
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu-26.04-arm, ubuntu-22.04-arm]
- target: [GCC=1, CLANG=1]
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v7
- - name: Run Tests ${{ matrix.target }} on ${{ matrix.os }}
- run: |
- # The 'sched_policy00' needs the following:
- sudo sysctl -w kernel.sched_rt_runtime_us=-1
- # etc/hosts entry is needed for netns_lock_iptables
- echo "127.0.0.1 localhost" | sudo tee -a /etc/hosts
- sudo -E make -C scripts/ci local ${{ matrix.target }} RUN_TESTS=1 \
- ZDTM_OPTS="-x zdtm/static/change_mnt_context -x zdtm/static/maps05"
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- archlinux-test:
- name: Arch Linux Test (${{ matrix.shard_name }})
- needs: [alpine-test]
- # archlinux:latest + pacman -Syu is a rolling-release build; failures
- # caused by upstream package churn are outside CRIU's control.
- continue-on-error: true
- strategy:
- fail-fast: false
- matrix:
- shard: [0, 1, 2, 3, 4]
- include:
- - shard: 0
- shard_name: zdtm 1/4
- - shard: 1
- shard_name: zdtm 2/4
- - shard: 2
- shard_name: zdtm 3/4
- - shard: 3
- shard_name: zdtm 4/4
- - shard: 4
- shard_name: non-zdtm
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - name: Run Arch Linux ${{ matrix.shard_name }} Test
- run: >
- sudo -E make -C scripts/ci archlinux
- ZDTM_SHARD_INDEX=${{ matrix.shard }}
- ZDTM_SHARD_COUNT=4
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- centos-stream-test:
- name: CentOS Stream ${{ matrix.version }}
- # aarch64 is not supported by lima-vm/lima-actions
- # https://github.com/lima-vm/lima-actions/pull/1
- needs: [alpine-test]
- runs-on: ubuntu-24.04
- timeout-minutes: 60
- strategy:
- fail-fast: false
- matrix:
- version: [9, 10]
- steps:
- - uses: actions/checkout@v7
- - uses: ./.github/actions/lima-vm-setup
- with:
- template: centos-stream-${{ matrix.version }}
- cache-key-prefix: lima-centos-stream-${{ matrix.version }}
- - name: Setup VM
- run: lima sudo /home/criu/scripts/ci/lima.sh centos-stream-setup
- - name: Show VM info
- run: |
- lima uname -a
- lima cat /proc/cmdline
- - name: Run tests
- run: ssh -tt lima-default sudo -i /home/criu/scripts/ci/lima.sh centos-stream-test
- - name: Print dmesg
- if: always()
- run: lima sudo dmesg
-
- compat-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- strategy:
- fail-fast: false
- matrix:
- target: [GCC, CLANG]
- steps:
- - uses: actions/checkout@v7
- - name: Run Compat Tests (${{ matrix.target }})
- run: sudo -E make -C scripts/ci local COMPAT_TEST=y ${{ matrix.target }}=1
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- cross-compile:
- needs: [alpine-test]
- runs-on: ubuntu-latest
- continue-on-error: ${{ matrix.experimental }}
- strategy:
- fail-fast: false
- matrix:
- experimental: [false]
- target: [
- armv7-stable-cross,
- aarch64-stable-cross,
- ppc64-stable-cross,
- riscv64-stable-cross,
- ]
- include:
- - experimental: true
- target: armv7-unstable-cross
- - experimental: true
- target: aarch64-unstable-cross
- - experimental: true
- target: ppc64-unstable-cross
- steps:
- - uses: actions/checkout@v7
- - name: Run Cross Compilation Targets
- run: >
- sudo make -C scripts/ci ${{ matrix.target }}
- BUILD_OPTIONS="--build-arg CI_CROSS_COMPILE=1"
-
- docker-test:
- needs: [alpine-test]
- runs-on: ${{ matrix.os }}
- strategy:
- fail-fast: false
- matrix:
- os: [ubuntu-22.04]
- steps:
- - uses: actions/checkout@v7
- - name: Run Docker Test (${{ matrix.os }})
- run: sudo make -C scripts/ci docker-test
-
- fedora-asan-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - name: Run Fedora ASAN Test
- run: sudo -E make -C scripts/ci fedora-asan
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- fedora-rawhide-test:
- name: ${{ matrix.name }}
- needs: [alpine-test]
- runs-on: ${{ matrix.os }}
- strategy:
- fail-fast: false
- matrix:
- include:
- - os: ubuntu-22.04
- name: x86_64 Fedora Rawhide
- - os: ubuntu-24.04-arm
- name: aarch64 Fedora Rawhide
- steps:
- - uses: actions/checkout@v7
- - name: Run Fedora Rawhide Test
- # We need to pass environment variables from the CI environment to
- # distinguish between CI environments. However, we need to make sure that
- # XDG_RUNTIME_DIR environment variable is not set due to a bug in Podman.
- # FIXME: https://github.com/containers/podman/issues/14920
- run: sudo -E XDG_RUNTIME_DIR= make -C scripts/ci fedora-rawhide CONTAINER_RUNTIME=podman BUILD_OPTIONS="--security-opt seccomp=unconfined"
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- vm-fedora-rawhide-test:
- name: VM Fedora ${{ matrix.name }} based test
- needs: [alpine-test]
- runs-on: ubuntu-24.04
- timeout-minutes: 60
- strategy:
- fail-fast: false
- matrix:
- include:
- - variant: fedora-stable
- name: Stable
- reboot: true
- - variant: fedora-next
- name: Next
- reboot: true
- - variant: fedora-no-vdso
- name: No VDSO
- reboot: true
- - variant: fedora-non-root
- name: Non-Root
- reboot: false
- steps:
- - uses: actions/checkout@v7
- - uses: ./.github/actions/lima-vm-setup
- with:
- template: fedora
- cache-key-prefix: lima-fedora
- - name: Setup VM
- run: lima sudo /home/criu/scripts/ci/lima.sh ${{ matrix.variant }}-setup
- - name: Reboot VM to activate new kernel
- if: matrix.reboot
- run: |
- limactl stop default
- limactl start default
- - name: Show VM info
- run: |
- lima uname -a
- lima cat /proc/cmdline
- - name: Run tests
- run: ssh -tt lima-default sudo -i /home/criu/scripts/ci/lima.sh ${{ matrix.variant }}-test
- - name: Print dmesg
- if: always()
- run: lima sudo dmesg
-
- gcov-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - name: Run Coverage Tests
- run: sudo -E make -C scripts/ci local GCOV=1
- - name: Run gcov
- run: sudo -E find . -name '*gcda' -type f -print0 | sudo -E xargs --null --max-args 128 gcov
- - name: Upload coverage to Codecov
- uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7
- with:
- token: ${{ secrets.CODECOV_TOKEN }}
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- java-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - name: Run Java Test
- run: sudo make -C scripts/ci java-test
-
- loongarch64-qemu-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - run: sudo make -C scripts/ci loongarch64-qemu-test
-
- nftables-test:
- needs: [alpine-test]
- runs-on: ubuntu-26.04
- steps:
- - uses: actions/checkout@v7
- - name: Remove iptables
- run: sudo apt remove -y iptables
- - name: Install libnftables-dev
- run: sudo contrib/apt-install libnftables-dev
- - name: chmod 755 /home/runner
- # CRIU's tests are sometimes running as some random user and need
- # to be able to access the test files.
- run: sudo chmod 755 /home/runner
- - name: Build with nftables network locking backend
- run: sudo make -C scripts/ci local COMPILE_FLAGS="NETWORK_LOCK_DEFAULT=NETWORK_LOCK_NFTABLES"
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- podman-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - name: Run Podman Test
- run: sudo make -C scripts/ci podman-test
-
- stream-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - name: Run CRIU Image Streamer Test
- run: sudo -E make -C scripts/ci local STREAM_TEST=1
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- x86-64-clang-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - name: Run X86_64 CLANG Test
- run: sudo make -C scripts/ci x86_64 CLANG=1
- - name: Print dmesg
- if: always()
- run: sudo dmesg
-
- x86-64-gcc-test:
- needs: [alpine-test]
- runs-on: ubuntu-22.04
- steps:
- - uses: actions/checkout@v7
- - name: Run X86_64 GCC Test
- run: sudo make -C scripts/ci x86_64
- - name: Print dmesg
- if: always()
- run: sudo dmesg
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
deleted file mode 100644
index 102a505fc..000000000
--- a/.github/workflows/codeql.yml
+++ /dev/null
@@ -1,50 +0,0 @@
-name: "CodeQL"
-
-on:
- push:
- branches: [ "criu-dev", "master" ]
- pull_request:
- branches: [ "criu-dev" ]
- schedule:
- - cron: "11 6 * * 3"
-
-# Cancel any preceding run on the pull request.
-concurrency:
- group: codeql-test-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: ${{ github.ref != 'refs/heads/criu-dev' }}
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- strategy:
- fail-fast: false
- matrix:
- language: [ python, cpp ]
-
- steps:
- - name: Checkout
- uses: actions/checkout@v7
-
- - name: Install Packages (cpp)
- if: ${{ matrix.language == 'cpp' }}
- run: |
- sudo contrib/apt-install protobuf-c-compiler libprotobuf-c-dev libprotobuf-dev build-essential libprotobuf-dev libprotobuf-c-dev protobuf-c-compiler protobuf-compiler python3-protobuf libnet-dev pkg-config libnl-3-dev libbsd0 libbsd-dev iproute2 libcap-dev libaio-dev libbsd-dev python3-yaml libnl-route-3-dev gnutls-dev
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v4
- with:
- languages: ${{ matrix.language }}
- queries: +security-and-quality
-
- - name: Autobuild
- uses: github/codeql-action/autobuild@v4
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v4
- with:
- category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/cross-compile-daily.yml b/.github/workflows/cross-compile-daily.yml
deleted file mode 100644
index 9ca0ec4ba..000000000
--- a/.github/workflows/cross-compile-daily.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: Daily Cross Compile Tests
-
-on:
- schedule:
- - cron: '30 12 * * *'
-
-jobs:
- build:
-
- runs-on: ubuntu-latest
- strategy:
- matrix:
- target: [armv7-stable-cross, aarch64-stable-cross, ppc64-stable-cross, mips64el-stable-cross, riscv64-stable-cross]
- branches: [criu-dev, master]
-
- steps:
- - uses: actions/checkout@v7
- with:
- ref: ${{ matrix.branches }}
- - name: Run Cross Compilation Targets
- run: >
- sudo make -C scripts/ci ${{ matrix.target }}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
deleted file mode 100644
index 0a0aca88a..000000000
--- a/.github/workflows/lint.yml
+++ /dev/null
@@ -1,40 +0,0 @@
-name: Run code linter
-
-on: [push, pull_request]
-
-# Cancel any preceding run on the pull request.
-concurrency:
- group: lint-test-${{ github.event.pull_request.number || github.ref }}
- cancel-in-progress: ${{ github.ref != 'refs/heads/criu-dev' }}
-
-jobs:
- build:
- runs-on: ubuntu-latest
- container:
- image: registry.fedoraproject.org/fedora:latest
- steps:
- - name: Install tools
- run: sudo dnf -y install git make ruff xz clang-tools-extra codespell git-clang-format ShellCheck
-
- - uses: actions/checkout@v7
-
- - name: Set git safe directory
- # https://github.com/actions/checkout/issues/760
- run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
-
- - name: Run make lint
- run: make lint
-
- - name: Run make indent
- continue-on-error: true
- run: |
- if [ -z "${{github.base_ref}}" ]; then
- git fetch --deepen=1
- make indent
- else
- git fetch origin ${{github.base_ref}}
- make indent BASE=origin/${{github.base_ref}}
- fi
- - name: Raise in-line make indent warnings
- run: |
- git diff | ./scripts/github-indent-warnings.py
diff --git a/.github/workflows/linux-next.yml b/.github/workflows/linux-next.yml
deleted file mode 100644
index 5aebffe82..000000000
--- a/.github/workflows/linux-next.yml
+++ /dev/null
@@ -1,402 +0,0 @@
-name: linux-next-tests
-on:
- schedule:
- - cron: "20 8 * * 0"
- workflow_dispatch:
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-#
-# This workflow is to test CRIU on linux-next tree.
-# It involves external GitHub Actions runners (EC2 instances on AWS).
-# To run it requires necessary setup on AWS side (auth via GitHub OIDC to
-# manage EC2 instances and S3 bucket), and GH_RUNNERS_PAT_TOKEN
-# (GitHub Personal Access Token to manage GitHub Actions Runners for a repo).
-#
-# Logic is simple:
-# 1. Build linux kernel on GitHub-hosted runner and upload .deb-packages
-# to S3 bucket
-# 2. Create EC2 instance and register it as GH self-hosted runner
-# (we use machulav/ec2-github-runner for this)
-# 3. Schedule a job on newly created runner, install a new kernel
-# 4. Reboot EC2 instance
-# 5. Schedule CRIU test job on EC2 runner
-# 6. Destroy EC2 instance and unregister it from GitHub.
-#
-# I use explicit commit hashes for actions steps which are not GitHub-provided
-# (for example machulav/ec2-github-runner@343a1b2ae682e681c3cec9a235d882da17ff04ef).
-# This is to be on a safe side and ensure that any update is done manually.
-#
-
-permissions:
- id-token: write
-
-jobs:
- kernel-build:
- env:
- BUILDDIR: "/home/runner/kernel-build/tmp"
- name: Kernel build
- runs-on: ubuntu-24.04
- outputs:
- date: ${{ steps.get-date.outputs.date }}
-
- steps:
- - name: Checkout
- uses: actions/checkout@v6
-
- - name: Prepare directory for build
- run: |
- set -eux
-
- echo $BUILDDIR
- mkdir -p $BUILDDIR
- # Not enough RAM for kernel builds anymore
- # sudo mount -t tmpfs tmpfs $BUILDDIR
-
- - name: Get Date
- id: get-date
- run: |
- echo "date=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT
-
- - name: Restore linux-next build artifacts from cache
- id: build-artifacts-restore
- uses: actions/cache@v5
- with:
- path: ${{ env.BUILDDIR }}/artifacts
- key: linux-next-build-${{ steps.get-date.outputs.date }}
-
- - name: Install dependencies
- if: steps.build-artifacts-restore.outputs.cache-hit != 'true'
- run: |
- set -eux
-
- sudo apt-get update
-
- sudo apt-get install --no-install-recommends -y \
- curl \
- git \
- build-essential \
- libssl-dev \
- libelf-dev \
- libdw-dev \
- bc \
- bison \
- cpio \
- flex \
- debhelper-compat
-
- # We don't want to put too much stress to git.kernel.org so we
- # use a cache (valid for 1 day) for git clone copy of the repo.
- - name: Restore linux-next git cache
- if: steps.build-artifacts-restore.outputs.cache-hit != 'true'
- id: linux-next-git-restore
- uses: actions/cache@v5
- with:
- path: ${{ env.BUILDDIR }}/linux
- key: linux-next-git-cache-${{ steps.get-date.outputs.date }}
-
- - name: Checkout Linux kernel
- if: ${{ steps.build-artifacts-restore.outputs.cache-hit != 'true' &&
- steps.linux-next-git-restore.outputs.cache-hit != 'true' }}
- run: |
- set -eux
-
- cd "$BUILDDIR"
-
- git clone --depth=1 --branch master --single-branch \
- https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git linux
-
- # Record exactly which linux-next state we built, so a CI failure
- # can be correlated with a specific tree (and its daily next-* tag).
- git -C linux --no-pager log -1 --format='linux-next HEAD: %H %cs %s'
-
- - name: Save linux-next git cache
- if: ${{ steps.build-artifacts-restore.outputs.cache-hit != 'true' &&
- steps.linux-next-git-restore.outputs.cache-hit != 'true' }}
- uses: actions/cache/save@v5
- with:
- path: |
- ${{ env.BUILDDIR }}/linux
- key: linux-next-git-cache-${{ steps.get-date.outputs.date }}
-
- - name: Configure AWS Credentials
- uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # 6.2.1
- with:
- role-to-assume: ${{ vars.AWS_IAM_ROLE }}
- aws-region: ${{ vars.AWS_REGION }}
-
- - name: Build Linux kernel
- if: steps.build-artifacts-restore.outputs.cache-hit != 'true'
- run: |
- set -eux
-
- cd "$BUILDDIR/linux"
-
- # kernel config was generated with make localmodconfig on EC2 instance
- # after full CRIU tests run (to trigger all modules load)
- aws s3 cp \
- s3://criu-linux-next-ci-634567146514-eu-central-1-an/t3.xlarge-kernel-config \
- .config
-
- scripts/config --disable SYSTEM_TRUSTED_KEYS
- scripts/config --disable SYSTEM_REVOCATION_KEYS
-
- make olddefconfig
-
- time make -j$(nproc) bindeb-pkg LOCALVERSION="-criu-ci"
-
- ls -la .
- ls -la ..
-
- cd "$BUILDDIR"
- mkdir artifacts/
- mv linux-*.deb artifacts/
-
- echo "artifactPath=${BUILDDIR}/artifacts" >> $GITHUB_ENV
-
- - name: Save linux-next build results cache
- if: steps.build-artifacts-restore.outputs.cache-hit != 'true'
- uses: actions/cache/save@v5
- with:
- path: ${{ env.artifactPath }}
- key: linux-next-build-${{ steps.get-date.outputs.date }}
-
- - name: Upload kernel packages to S3
- run: |
- set -eux
-
- cd "$BUILDDIR"
-
- BUILD_DATE="${{ steps.get-date.outputs.date }}"
-
- aws s3 cp \
- artifacts \
- s3://criu-linux-next-ci-634567146514-eu-central-1-an/artifacts/$BUILD_DATE/ \
- --recursive
-
- - uses: actions/upload-artifact@v7
- with:
- name: linux-kernel-build
- path: ${{ env.BUILDDIR }}/artifacts/linux-*.deb
- if-no-files-found: error
- retention-days: 14
-
- start-runner:
- name: Start self-hosted EC2 runner
- needs:
- - kernel-build
- runs-on: ubuntu-latest
- outputs:
- label: ${{ steps.start-ec2-runner.outputs.label }}
- ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }}
- steps:
- - name: Configure AWS Credentials
- uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # 6.2.1
- with:
- role-to-assume: ${{ vars.AWS_IAM_ROLE }}
- aws-region: ${{ vars.AWS_REGION }}
-
- - name: Start EC2 runner
- id: start-ec2-runner
- uses: machulav/ec2-github-runner@343a1b2ae682e681c3cec9a235d882da17ff04ef # 2.6.1
- with:
- mode: start
- startup-timeout-minutes: 10
- github-token: ${{ secrets.GH_RUNNERS_PAT_TOKEN }}
- ec2-image-id: ami-0596cf3199908321b # Ubuntu 24.04 LTS image id on AWS
- ec2-instance-type: t3.xlarge
- subnet-id: subnet-0ddb356c3fc41e51a
- security-group-id: sg-054aa948984162822
- packages: '["git", "docker.io"]'
- runner-debug: true
-
- # we need this so GitHub runner software survives EC2 reboot
- run-runner-as-service: true
-
- # we need this to make EC2 runner capable of accessing S3 bucket with kernel builds
- iam-role-name: ec2-linux-next-vm
-
- aws-resource-tags: > # attach tags to distinguish GH Actions instances on AWS
- [
- {"Key": "Name", "Value": "ec2-github-runner"},
- {"Key": "GitHubRepository", "Value": "${{ github.repository }}"},
- {"Key": "GitHubRunId", "Value": "${{ github.run_id }}"}
- ]
-
- install-kernel:
- name: Install kernel on the EC2 runner
- needs:
- - kernel-build
- - start-runner # required to start the main job when the runner is ready
- runs-on: ${{ needs.start-runner.outputs.label }} # run the job on the newly created runner
- timeout-minutes: 15
- steps:
- - name: Install unzip
- run: |
- set -eux
-
- sudo apt-get update
-
- sudo apt-get install --no-install-recommends -y \
- unzip
-
- - name: Install AWS CLI
- run: |
- set -eux
-
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip awscliv2.zip
- sudo ./aws/install --update
-
- - name: Install linux-next kernel
- run: |
- set -eux
-
- BUILD_DATE="${{ needs.kernel-build.outputs.date }}"
-
- aws s3 cp \
- s3://criu-linux-next-ci-634567146514-eu-central-1-an/artifacts/$BUILD_DATE/ \
- artifacts \
- --recursive
-
- ls -la artifacts
-
- sudo dpkg -i artifacts/linux-image-*-criu-ci_*_amd64.deb
-
- reboot-runner:
- name: Reboot EC2 runner instance
- needs:
- - start-runner
- - install-kernel # required to start the main job when the runner is ready
- runs-on: ubuntu-latest
- timeout-minutes: 15
- steps:
- - name: Configure AWS Credentials
- uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # 6.2.1
- with:
- role-to-assume: ${{ vars.AWS_IAM_ROLE }}
- aws-region: ${{ vars.AWS_REGION }}
-
- - name: Reboot runner
- run: |
- set -eux
-
- INSTANCE_ID="${{ needs.start-runner.outputs.ec2-instance-id }}"
- aws ec2 reboot-instances --instance-ids "$INSTANCE_ID"
- aws ec2 wait instance-status-ok --instance-ids "$INSTANCE_ID"
-
- do-the-job:
- name: Do the job on the runner
- needs:
- - start-runner
- - reboot-runner # required to start the main job when the runner is ready
- runs-on: ${{ needs.start-runner.outputs.label }} # run the job on the newly created runner
- timeout-minutes: 60
- steps:
- - name: Validate kernel
- run: |
- set -eux
-
- # ensure that we are running linux-next kernel
- uname -a | grep next | grep "criu-ci"
-
- - name: Install make
- run: |
- set -eux
-
- sudo apt-get install --no-install-recommends -y \
- make
-
- - name: Checkout
- uses: actions/checkout@v6
-
- - name: Run CRIU tests
- run: |
- set -eux
-
- #
- # We have to set oom_score_adj to the minimal possible value,
- # otherwise zdtm/static/oom_score_adj test will fail with EACCES
- # check (see __set_oom_adj() [1] in the kernel).
- #
- # This is not a problem on a normal GitHub runners, but is a problem
- # on a self-hosted ones cause we run GitHub stuff via systemd service
- # and systemd sets oom_score_adj on a parent process to 500, while
- # in the test we want to set 400.
- #
- # [1] https://github.com/torvalds/linux/blob/27fa82620cbaa89a7fc11ac3057701d598813e87/fs/proc/base.c#L1151
- echo "-1000" > /proc/self/oom_score_adj
-
- echo 131072 > /sys/kernel/debug/tracing/buffer_size_kb
- echo 1 > /sys/kernel/tracing/events/x86_fpu/x86_fpu_xstate_check_failed/enable
- echo 1 > /sys/kernel/tracing/tracing_on
-
- sudo make -C scripts/ci local
-
- - name: Print trace buffer
- run: |
- set -eux
-
- cat /sys/kernel/tracing/trace
-
- ! grep x86_fpu_xstate_check_failed /sys/kernel/tracing/trace
-
- - name: Get tainted state
- run: |
- set -eux
-
- cat /proc/sys/kernel/tainted
-
- - name: Print dmesg
- if: always()
- run: |
- set -eux
-
- dmesg
-
- get-serial-console:
- name: Get EC2 serial console output
- needs:
- - start-runner
- - do-the-job # required to wait when the main job is done
- if: ${{ always() }} # required to get logs even if the error happened in the previous jobs
- runs-on: ubuntu-latest
- steps:
- - name: Configure AWS Credentials
- uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # 6.2.1
- with:
- role-to-assume: ${{ vars.AWS_IAM_ROLE }}
- aws-region: ${{ vars.AWS_REGION }}
-
- - name: Get serial console output
- run: |
- set -eux
-
- INSTANCE_ID="${{ needs.start-runner.outputs.ec2-instance-id }}"
- aws ec2 get-console-output \
- --instance-id "$INSTANCE_ID" \
- --latest \
- --output text
-
- stop-runner:
- name: Stop self-hosted EC2 runner
- needs:
- - start-runner # required to get output from the start-runner job
- - get-serial-console
- if: ${{ always() }} # required to stop the runner even if the error happened in the previous jobs
- runs-on: ubuntu-latest
- steps:
- - name: Configure AWS Credentials
- uses: aws-actions/configure-aws-credentials@254c19bd240aabef8777f48595e9d2d7b972184b # 6.2.1
- with:
- role-to-assume: ${{ vars.AWS_IAM_ROLE }}
- aws-region: ${{ vars.AWS_REGION }}
- - name: Stop EC2 runner
- uses: machulav/ec2-github-runner@343a1b2ae682e681c3cec9a235d882da17ff04ef # 2.6.1
- with:
- mode: stop
- github-token: ${{ secrets.GH_RUNNERS_PAT_TOKEN }}
- label: ${{ needs.start-runner.outputs.label }}
- ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }}
diff --git a/.github/workflows/manage-labels.yml b/.github/workflows/manage-labels.yml
deleted file mode 100644
index 693e404ad..000000000
--- a/.github/workflows/manage-labels.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: Remove labels
-on: [issue_comment, pull_request_review_comment]
-jobs:
- remove-labels-on-comments:
- name: Remove labels on comments
- if: github.event_name == 'issue_comment'
- runs-on: ubuntu-latest
- steps:
- - uses: mondeja/remove-labels-gh-action@b7118e4ba5dca74acf1059b3cb7660378ff9ab1a # v2
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- labels: |
- changes requested
- awaiting reply
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
deleted file mode 100644
index 4b1c3800f..000000000
--- a/.github/workflows/stale.yml
+++ /dev/null
@@ -1,27 +0,0 @@
-name: Mark stale issues and pull requests
-
-# Please refer to https://github.com/actions/stale/blob/master/action.yml
-# to see all config knobs of the stale action.
-
-on:
- schedule:
- - cron: "0 0 * * *"
-
-jobs:
- stale:
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/stale@v10
- with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- stale-issue-message: 'A friendly reminder that this issue had no activity for 30 days.'
- stale-pr-message: 'A friendly reminder that this PR had no activity for 30 days.'
- stale-issue-label: 'stale-issue'
- stale-pr-label: 'stale-pr'
- days-before-stale: 30
- days-before-close: 365
- remove-stale-when-updated: true
- exempt-pr-labels: 'no-auto-close'
- exempt-issue-labels: 'no-auto-close,new feature,enhancement'
diff --git a/.gitignore b/.gitignore
index 94daa13ea..3f284972a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,35 +1,32 @@
-.config
*.o
*.d
-*.a
*.img
*.bin
*.elf
*.out
*.swp
*.swo
+*-blob.h
*.so
.git-ignore
*.patch
*.pyc
+criu
cscope*
tags
TAGS
-Makefile.local
-compel/compel
-compel/compel-host-bin
-images/*.c
-images/*.h
-.gitid
-criu/criu
-criu/unittest/unittest
-criu/include/version.h
-criu/pie/restorer-blob.h
-criu/pie/parasite-blob.h
-criu/protobuf-desc-gen.h
-lib/build/
-lib/c/criu.pc
-compel/include/asm
-include/common/asm
-include/common/config.h
-build/**
+syscall-x86-64.S
+include/syscall.h
+include/syscall-codes.h
+protobuf/*.c
+protobuf/*.h
+protobuf/google/protobuf/*.c
+protobuf/google/protobuf/*.h
+include/version.h
+arch/x86/sys-exec-tbl.c
+arch/x86/syscalls.S
+pie/pie.lds.S
+include/config.h
+protobuf-desc-gen.h
+criu.pc
+build
diff --git a/.lgtm.yml b/.lgtm.yml
deleted file mode 100644
index 4beadcc63..000000000
--- a/.lgtm.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-extraction:
- cpp:
- prepare:
- packages:
- - "protobuf-c-compiler"
- - "libprotobuf-c-dev"
- - "libprotobuf-dev"
- - "build-essential"
- - "libprotobuf-dev"
- - "libprotobuf-c-dev"
- - "protobuf-c-compiler"
- - "protobuf-compiler"
- - "python3-protobuf"
- - "libnet-dev"
- - "pkg-config"
- - "libnl-3-dev"
- - "libbsd0"
- - "libbsd-dev"
- - "iproute2"
- - "libcap-dev"
- - "libaio-dev"
- - "libbsd-dev"
- - "python3-yaml"
- - "libnl-route-3-dev"
- - "gnutls-dev"
diff --git a/.mailmap b/.mailmap
index 8076f0bc9..d8c3f594d 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,10 +1,6 @@
Stanislav Kinsbursky
Pavel Emelyanov
-Andrei Vagin
-Andrei Vagin
-Andrei Vagin
-Andrei Vagin
-Andrei Vagin
+Andrey Vagin
+Andrey Vagin
+Andrey Vagin Andrew Vagin
Cyrill Gorcunov
-Alexander Mikhalitsyn
-Alexander Mikhalitsyn
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..56834642a
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: c
+env:
+ - ARCH=arm
+ - ARCH=x86_64
+compiler:
+ - gcc
+before_install:
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq protobuf-c-compiler libprotobuf-c0-dev libaio-dev libprotobuf-dev protobuf-compiler python-ipaddr
+script:
+ - "bash -ex scripts/travis-ci.sh"
diff --git a/CLAUDE.md b/CLAUDE.md
deleted file mode 120000
index e3c5a92d9..000000000
--- a/CLAUDE.md
+++ /dev/null
@@ -1 +0,0 @@
-GEMINI.md
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index a52484f7c..000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,449 +0,0 @@
-## How to contribute to CRIU
-
-CRIU project is (almost) the never-ending story, because we have to always keep up with the
-Linux kernel supporting checkpoint and restore for all the features it provides. Thus we're
-looking for contributors of all kinds -- feedback, bug reports, testing, coding, writing, etc.
-Here are some useful hints to get involved.
-
-* We have both -- [very simple](https://github.com/checkpoint-restore/criu/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement) and [more sophisticated](https://github.com/checkpoint-restore/criu/issues?q=is%3Aissue+is%3Aopen+label%3A%22new+feature%22) coding tasks;
-* CRIU does need [extensive testing](https://github.com/checkpoint-restore/criu/issues?q=is%3Aissue+is%3Aopen+label%3Atesting);
-* Documentation is always hard, we have [some information](https://criu.org/Category:Empty_articles) that is to be extracted from people's heads into wiki pages as well as [some texts](https://criu.org/Category:Editor_help_needed) that all need to be converted into useful articles;
-* Feedback is expected on the GitHub issues page and on the [mailing list](https://lore.kernel.org/criu);
-* We accept GitHub pull requests and this is the preferred way to contribute to CRIU. If you prefer to send patches by email, you are welcome to send them to [CRIU development mailing list](https://lore.kernel.org/criu).
-Below we describe in more detail recommend practices for CRIU development.
-* Spread the word about CRIU in [social networks](http://criu.org/Contacts);
-* If you're giving a talk about CRIU -- let us know, we'll mention it on the [wiki main page](https://criu.org/News/events);
-
-### Setting up the development environment
-
-Although `criu` could be run as non-root (see [Security](https://criu.org/Security)), development is better to be done as root. For example, some tests require root. So, it would be a good idea to set up some recent Linux distro on a virtual machine.
-
-### Get the source code
-
-The CRIU sources are tracked by Git. Official CRIU repo is at https://github.com/checkpoint-restore/criu.
-
-The repository may contain multiple branches. Development happens in the **criu-dev** branch.
-
-To clone CRIU repo and switch to the proper branch, run:
-
-```
-git clone https://github.com/checkpoint-restore/criu criu
-cd criu
-git checkout criu-dev
-```
-
-### Building from source
-
-Follow these steps to compile CRIU from source code.
-
-#### Installing build dependencies
-
-First, you need to install the required build dependencies. We provide scripts to simplify this process for several Linux distributions in [contrib/dependencies](contrib/dependencies). For a complete list of dependencies, please refer to the [installation guide](https://criu.org/Installation).
-
-##### On Ubuntu/Debian-based systems:
-
-```
-./contrib/dependencies/apt-packages.sh
-```
-
-##### On Fedora/CentOS-based systems:
-
-```
-./contrib/dependencies/dnf-packages.sh
-```
-
-##### Using Nix:
-
-```
-nix develop
-```
-
-#### Compiling CRIU
-
-Once the dependencies are installed, you can compile CRIU by running the `make` command from the root of the source directory:
-
-```
-make
-```
-
-This should create the `./criu/criu` executable.
-
-## Edit the source code
-
-When you change the source code, please keep in mind the following code conventions:
-
-* code is written to be read, so the code readability is the most important thing you need to have in mind when preparing patches
-* we prefer tabs and indentations to be 8 characters width
-* we prefer line length of 80 characters or less, more is allowed if it helps with code readability
-* CRIU mostly follows [Linux kernel coding style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but we are less strict than the kernel community
-
-Other conventions can be learned from the source code itself. In short, make sure your new code looks similar to what is already there.
-
-## Automatic tools to fix coding-style
-
-Important: These tools are there to advise you, but should not be considered as a "source of truth", as tools also make nasty mistakes from time to time which can completely break code readability.
-
-The following command can be used to automatically run a code linter for Python files (ruff), Shell scripts (shellcheck),
-text spelling (codespell), and a number of CRIU-specific checks (usage of print macros and EOL whitespace for C files).
-
-```
-make lint
-```
-
-In addition, we have adopted a [clang-format configuration file](https://www.kernel.org/doc/Documentation/process/clang-format.rst)
-based on the kernel source tree. However, compliance with the clang-format autoformat rules is optional. If the automatic code formatting
-results in decreased readability, we may choose to ignore these errors.
-
-Run the following command to check if your changes are compliant with the clang-format rules:
-
-```
-make indent
-```
-
-This command is built upon the `git-clang-format` tool and supports two options `BASE` and `OPTS`. The `BASE` option allows you to
-specify a range of commits to check for coding style issues. By default, it is set to `HEAD~1`, so that only the last commit is checked.
-If you are developing on top of the criu-dev branch and want to check all your commits for compliance with the clang-format rules, you
-can use `BASE=origin/criu-dev`. The `OPTS` option can be used to pass additional options to `git-clang-format`. For example, if you want
-to check the last *N* commits for formatting errors, without applying the changes to the codebase you can use the following command.
-
-```
-make indent OPTS=--diff BASE=HEAD~N
-```
-
-Note that for pull requests, the "Run code linter" workflow runs these checks for all commits. If a clang-format error is detected
-we need to review the suggested changes and decide if they should be fixed before merging.
-
-Here are some bad examples of clang-format-ing:
-
-* if clang-format tries to force 120 characters and breaks readability - it is wrong:
-
-```
-@@ -58,8 +59,7 @@ static int register_membarriers(void)
- }
-
- if (!all_ok) {
-- fail("can't register membarrier()s - tried %#x, kernel %#x",
-- barriers_registered, barriers_supported);
-+ fail("can't register membarrier()s - tried %#x, kernel %#x", barriers_registered, barriers_supported);
- return -1;
- }
-```
-
-* if clang-format breaks your beautiful readability friendly alignment in structures, comments or defines - it is wrong:
-
-```
---- a/test/zdtm/static/membarrier.c
-+++ b/test/zdtm/static/membarrier.c
-@@ -27,9 +27,10 @@ static const struct {
- int register_cmd;
- int execute_cmd;
- } membarrier_cmds[] = {
-- { "", MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, MEMBARRIER_CMD_PRIVATE_EXPEDITED },
-- { "_SYNC_CORE", MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE, MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE },
-- { "_RSEQ", MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ },
-+ { "", MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, MEMBARRIER_CMD_PRIVATE_EXPEDITED },
-+ { "_SYNC_CORE", MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE,
-+ MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE },
-+ { "_RSEQ", MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ, MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ },
- };
-```
-
-## Test your changes
-
-CRIU comes with an extensive test suite. To check whether your changes introduce any regressions, run
-
-```
-make test
-```
-
-The command runs [ZDTM Test Suite](https://criu.org/ZDTM_Test_Suite). Check for any error messages produced by it.
-
-## Describe your changes
-
-Describe your problem. Whether your change is a one-line bug fix or
-5000 lines of a new feature, there must be an underlying problem that
-motivated you to do this work. Convince the reviewer that there is a
-problem worth fixing and that it makes sense for them to read past the
-first paragraph.
-
-Once the problem is established, describe what you are actually doing
-about it in technical detail. It's important to describe the change
-in plain English for the reviewer to verify that the code is behaving
-as you intend it to.
-
-Solve only one problem per commit. If your description starts to get
-long, that's a sign that you probably need to split up your commit.
-See [Separate your changes](#separate-your-changes).
-
-Describe your changes in imperative mood, e.g. "make xyzzy do frotz"
-instead of "[This commit] makes xyzzy do frotz" or "[I] changed xyzzy
-to do frotz", as if you are giving orders to the codebase to change
-its behaviour.
-
-If your change fixes a bug in a specific commit, e.g. you found an issue using
-`git bisect`, please use the `Fixes:` tag with the abbreviation of
-the SHA-1 ID, and the one line summary. For example:
-
-```
-Fixes: 9433b7b9db3e ("make: use cflags/ldflags for config.h detection mechanism")
-```
-
-The following `git config` settings can be used to add a pretty format for
-outputting the above style in the `git log` or `git show` commands:
-
-```
-[pretty]
- fixes = Fixes: %h (\"%s\")
-```
-
-If your change address an issue listed in GitHub, please use `Fixes:` tag with the number of the issue. For instance:
-
-```
-Fixes: #339
-```
-
-The `Fixes:` tags should be put at the end of the detailed description.
-
-Please add a prefix to your commit subject line describing the part of the
-project your change is related to. This can be either the name of the file or
-directory you changed, or just a general word. If your patch is touching
-multiple components you may separate prefixes with "/"-es. Here are some good
-examples of subject lines from git log:
-
-```
-criu-ns: Convert to python3 style print() syntax
-compel: Calculate sh_addr if not provided by linker
-style: Enforce kernel style -Wstrict-prototypes
-rpc/libcriu: Add lsm-profile option
-```
-
-You may refer to [How to Write a Git Commit
-Message](https://chris.beams.io/posts/git-commit/) article for
-recommendations for good commit message.
-
-## Separate your changes
-
-Separate each **logical change** into a separate commit.
-
-For example, if your changes include both bug fixes and performance
-enhancements for a single driver, separate those changes into two
-or more commits. If your changes include an API update, and a new
-driver which uses that new API, separate those into two commits.
-
-On the other hand, if you make a single change to numerous files,
-group those changes into a single commit. Thus a single logical change
-is contained within a single commit.
-
-The point to remember is that each commit should make an easily understood
-change that can be verified by reviewers. Each commit should be justifiable
-on its own merits.
-
-When dividing your change into a series of commits, take special care to
-ensure that CRIU builds and runs properly after each commit in the
-series. Developers using `git bisect` to track down a problem can end up
-splitting your patch series at any point; they will not thank you if you
-introduce bugs in the middle.
-
-## Sign your work
-
-To improve tracking of who did what, we ask you to sign off the commits in
-your fork of CRIU or the patches that are to be emailed.
-
-The sign-off is a simple line at the end of the explanation for the
-patch, which certifies that you wrote it or otherwise have the right to
-pass it on as an open-source patch. The rules are pretty simple: if you
-can certify the below:
-
-### Developer's Certificate of Origin 1.1
- By making a contribution to this project, I certify that:
-
- (a) The contribution was created in whole or in part by me and I
- have the right to submit it under the open source license
- indicated in the file; or
-
- (b) The contribution is based upon previous work that, to the best
- of my knowledge, is covered under an appropriate open source
- license and I have the right under that license to submit that
- work with modifications, whether created in whole or in part
- by me, under the same open source license (unless I am
- permitted to submit under a different license), as indicated
- in the file; or
-
- (c) The contribution was provided directly to me by some other
- person who certified (a), (b) or (c) and I have not modified
- it.
-
- (d) I understand and agree that this project and the contribution
- are public and that a record of the contribution (including all
- personal information I submit with it, including my sign-off) is
- maintained indefinitely and may be redistributed consistent with
- this project or the open source license(s) involved.
-
-then you just add a line saying
-
-```
-Signed-off-by: Random J Developer
-```
-
-using your real name (please, no pseudonyms or anonymous contributions if
-it possible).
-
-Hint: you can use `git commit -s` to add Signed-off-by line to your
-commit message. To append such line to a commit you already made, use
-`git commit --amend -s`.
-
-```
- From: Random J Developer
-Subject: [PATCH] component: Short patch description
-
-Long patch description (could be skipped if patch
-is trivial enough)
-
-Signed-off-by: Random J Developer
----
-Patch body here
-```
-
-## AI-assisted contributions
-
-Use this tag when AI tools meaningfully contribute to the code,
-design, or commit message. Trivial use (e.g. basic autocomplete)
-does not require attribution. Following the
-[Linux kernel guidance on coding assistants](https://docs.kernel.org/process/coding-assistants.html),
-the tag format is:
-
-```
-Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]
-```
-
-Where `AGENT_NAME` identifies the AI tool or framework, `MODEL_VERSION`
-specifies which model was used, and the optional `[TOOL1] [TOOL2]`
-fields list any specialized analysis tools (e.g. coccinelle, sparse,
-smatch, clang-tidy) that were used alongside the AI assistant. Basic
-development tools (git, gcc, make, editors) should not be listed.
-
-For example:
-
-```
-Assisted-by: Claude:claude-3-opus coccinelle sparse
-```
-
-The `Assisted-by` tag should be placed after the commit message body
-and before the `Signed-off-by` line.
-
-Note that AI agents should not add `Signed-off-by` tags. Only human
-developers can certify the Developer's Certificate of Origin. The
-submitter is responsible for reviewing all AI-generated code and
-ensuring its correctness and license compliance.
-
-## Submit your work upstream
-
-We accept GitHub pull requests and this is the preferred way to contribute to CRIU.
-For that you should push your work to your fork of CRIU at [GitHub](https://github.com) and create a [pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests)
-
-### Pull request guidelines
-
-Pull request comment should contain description of the problem your changes
-solve and a brief outline of the changes included in the pull request.
-
-Please avoid pushing fixup commits to an existent pull request. Each commit
-should be self contained and there should not be fixup commits in a patch
-series. Pull requests that contain one commit which breaks something
-and another commit which fixes it, will be rejected.
-
-Please merge the fixup commits into the commits that has introduced the
-problem before creating a pull request.
-
-It may happen that the reviewers were not completely happy with your
-changes and requested changes to your patches. After you updated your
-changes please close the old pull request and create a new one that
-contains the following:
-
-* Description of the problem your changes solve and a brief outline of the
- changes
-* Link to the previous version of the pull request
-* Brief description of the changes between old and new versions of the pull
- request. If there were more than one previous pull request, all the
- revisions should be listed. For example:
-
-```
-v3: rebase on the current criu-dev
-v2: add commit to foo() and update bar() coding style
-```
-
-If there are only minor updates to the commits in a pull request, it is
-possible to force-push them into an existing pull request. This only applies
-to small changes and should be used with care. If you update an existing
-pull request, remember to add the description of the changes from the
-previous version.
-
-### Mailing list submission
-
-Historically, CRIU worked with mailing lists and patches so if you still prefer this way continue reading till the end of this section.
-
-### Make a patch
-
-To create a patch, run
-
-```
-git format-patch --signoff origin/criu-dev
-```
-
-You might need to read GIT documentation on how to prepare patches
-for mail submission. Take a look at http://book.git-scm.com/ and/or
-http://git-scm.com/documentation for details. It should not be hard
-at all.
-
-We recommend to post patches using `git send-email`
-
-```
-git send-email --cover-letter --no-chain-reply-to --annotate \
- --confirm=always --to=criu@lists.linux.dev criu-dev
-```
-
-Note that the `git send-email` subcommand may not be in
-the main git package and using it may require installation of a
-separate package, for example the "git-email" package in Fedora and
-Debian.
-
-If this is your first time using git send-email, you might need to
-configure it to point it to your SMTP server with something like:
-
-```
-git config --global sendemail.smtpServer stmp.example.net
-```
-
-If you get tired of typing `--to=criu@lists.linux.dev` all the time,
-you can configure that to be automatically handled as well:
-
-```
-git config sendemail.to criu@lists.linux.dev
-```
-
-If a developer is sending another version of the patch (e.g. to address
-review comments), they are advised to note differences to previous versions
-after the `---` line in the patch so that it helps reviewers but
-doesn't become part of git history. Moreover, such patch needs to be prefixed
-correctly with `--subject-prefix=PATCHv2` appended to
-`git send-email` (substitute `v2` with the correct
-version if needed though).
-
-### Mail patches
-
-The patches should be sent to CRIU development mailing list, `criu AT lists.linux.dev`. Note that you need to be subscribed first in order to post. The list web interface is available at https://lore.kernel.org/criu; you can also use standard mailman aliases to work with it.
-
-Please make sure the email client you're using doesn't screw your patch (line wrapping and so on).
-
-> **Note:** When sending a patch set that consists of more than one patch, please, push your changes in your local repo and provide the URL of the branch in the cover-letter
-
-### Wait for response
-
-Be patient. Most CRIU developers are pretty busy people so if
-there is no immediate response on your patch — don't be surprised,
-sometimes a patch may fly around a week before it gets reviewed.
-
-## Continuous integration
-
-Wiki article: [Continuous integration](https://criu.org/Continuous_integration)
-
-CRIU tests are run for each series sent to the mailing list. If you get a message from our patchwork that patches failed to pass the tests, you have to investigate what is wrong.
diff --git a/COPYING b/COPYING
index b04304ea0..dd0581f45 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-This software is licensed under the GNU GENERAL PUBLIC LICENCE Version
+This software is licenced under the GNU GENERAL PUBLIC LICENCE Version
2. Except that any software in the lib/ directory is for the creation of a
linkable library to the tools and is licensed under the GNU LESSER GENERAL
PUBLIC LICENCE Version 2.1. Contributing Authors agree that their code is
diff --git a/Documentation/.gitignore b/Documentation/.gitignore
index b4f39316b..ad3e4f05e 100644
--- a/Documentation/.gitignore
+++ b/Documentation/.gitignore
@@ -3,4 +3,3 @@
*.[1-8]
*.pdf
*.ps
-footer.txt
diff --git a/Documentation/HOWTO.cross-compile b/Documentation/HOWTO.cross-compile
index 44b19dfea..749c08113 100644
--- a/Documentation/HOWTO.cross-compile
+++ b/Documentation/HOWTO.cross-compile
@@ -1,10 +1,4 @@
-How to cross-compile CRIU on x86:
-
-Use the Dockerfile provided:
- scripts/build/Dockerfile.armv7-cross
-
-Historical guide how-to do it without docker container:
-[Unsupported, may not work anymore!]
+This HOWTO explains how to cross-compile CRIU on x86
1. Download the protobuf sources.
2. Apply the patch http://16918.selcdn.ru/crtools/aarch64/0001-protobuf-added-the-support-for-the-acrchitecture-AAr.patch
@@ -35,11 +29,3 @@ Historical guide how-to do it without docker container:
13. Compile CRIU:
ARCH= CROSS_COMPILE=$TARGET- CFLAGS=`pkg-config --cflags libprotobuf-c` LDFLAGS="`pkg-config --libs libprotobuf-c`" make
-
-Special notes for Android NDK cross compile:
-
-1, Android NDK doesn't have some headers required by CRIU build, they are ,
-
-2, Android NDK doesn't have some function required by CRIU build, they are aio*, fanotify_init, fanotify_mark, povit_root, index.
-
-3, in order to pass build with Android NDK, you implement them yourself, and link them to CRIU.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index de0cc448d..e236635e5 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -1,34 +1,20 @@
-__nmk_dir ?= ../scripts/nmk/scripts/
-include $(__nmk_dir)include.mk
-include $(__nmk_dir)macro.mk
+-include ../Makefile.inc
-ifneq ($(USE_ASCIIDOCTOR),)
-ASCIIDOC := asciidoctor
-XMLTO :=
-else
ASCIIDOC := asciidoc
+A2X := a2x
XMLTO := xmlto
-endif
-FOOTER := footer.txt
-SRC1 += crit.txt
-SRC1 += criu-ns.txt
-SRC1 += compel.txt
-SRC1 += criu-amdgpu-plugin.txt
-SRC8 += criu.txt
-SRC := $(SRC1) $(SRC8)
+SRC += criu.txt
XMLS := $(patsubst %.txt,%.xml,$(SRC))
-MAN1S := $(patsubst %.txt,%.1,$(SRC1))
-MAN8S := $(patsubst %.txt,%.8,$(SRC8))
-MANS := $(MAN1S) $(MAN8S)
-MAN1DIR := $(MANDIR)/man1
+MANS := $(patsubst %.txt,%.8,$(SRC))
MAN8DIR := $(MANDIR)/man8
-GROFF :=groff
-PAPER :=$(shell paperconf 2>/dev/null || echo letter)
-GROFF_OPTS := -Tps -t -dpaper=$(PAPER) -P-p$(PAPER) -man -msafer -rC1 -rD1 -rS11
-PSS := $(patsubst %,%.ps,$(basename $(MANS)))
-PDFS := $(patsubst %,%.pdf,$(basename $(MANS)))
+GROFF=groff
+PAPER=$(shell paperconf 2>/dev/null || echo letter)
+GROFF_OPTS := -Tps -t -dpaper=$(PAPER) -P-p$(PAPER) \
+ -man -msafer -rC1 -rD1 -rS11
+PSS := $(MANS:%.8=%.ps)
+PDFS := $(MANS:%.8=%.pdf)
all: check $(MANS)
ps: $(PSS)
@@ -36,66 +22,30 @@ pdf: $(PDFS)
.PHONY: all ps pdf check
check:
- $(Q) for B in $(ASCIIDOC) $(XMLTO); do \
+ $(Q) for B in $(ASCIIDOC) $(A2X) $(XMLTO); do \
$$B --version > /dev/null || exit 1; \
done
-ifeq ($(CRIU_VERSION),)
- include ../Makefile.versions
-endif
-$(FOOTER): ../Makefile.versions
- $(call msg-gen, $@)
- $(Q) echo ":doctype: manpage" > $@
- $(Q) echo ":man source: criu" >> $@
- $(Q) echo ":man version: $(CRIU_VERSION)" >> $@
- $(Q) echo ":man manual: CRIU Manual" >> $@
-
-%.1: %.txt $(FOOTER) custom.xsl
- $(call msg-gen, $@)
-ifneq ($(USE_ASCIIDOCTOR),)
- $(Q) $(ASCIIDOC) -b manpage -d manpage -o $@ $<
-else
- $(Q) $(ASCIIDOC) -b docbook -d manpage -o $(patsubst %.1,%.xml,$@) $<
- $(Q) $(XMLTO) man -m custom.xsl $(patsubst %.1,%.xml,$@)
-endif
-
-%.8: %.txt $(FOOTER) custom.xsl
- $(call msg-gen, $@)
-ifneq ($(USE_ASCIIDOCTOR),)
- $(Q) $(ASCIIDOC) -b manpage -d manpage -o $@ $<
-else
+%.8: %.txt
+ $(E) " GEN " $@
$(Q) $(ASCIIDOC) -b docbook -d manpage -o $(patsubst %.8,%.xml,$@) $<
- $(Q) $(XMLTO) man -m custom.xsl $(patsubst %.8,%.xml,$@)
-endif
-
-%.ps: %.1
- $(call msg-gen, $@)
- $(Q) $(GROFF) $(GROFF_OPTS) $^ > $@
+ $(Q) $(XMLTO) man --skip-validation $(patsubst %.8,%.xml,$@) 2>/dev/null
%.ps: %.8
- $(call msg-gen, $@)
+ $(E) " GEN " $@
$(Q) $(GROFF) $(GROFF_OPTS) $^ > $@
%.pdf: %.ps
- $(call msg-gen, $@)
+ $(E) " GEN " $@
$(Q) ps2pdf $< $@
clean:
- $(call msg-clean, "Documentation")
- $(Q) rm -f $(XMLS) $(MANS) $(PSS) $(PDFS) $(FOOTER)
+ $(E) " CLEAN "
+ $(Q) rm -f $(XMLS) $(MANS) $(PSS) $(PDFS)
-install: check $(MANS)
- $(E) " INSTALL " $(MAN8S)
+install: $(MANS)
+ $(E) " INSTALL " $(MANS)
$(Q) mkdir -p $(DESTDIR)$(MAN8DIR)
- $(Q) install -m 644 $(MAN8S) $(DESTDIR)$(MAN8DIR)
- $(E) " INSTALL " $(MAN1S)
- $(Q) mkdir -p $(DESTDIR)$(MAN1DIR)
- $(Q) install -m 644 $(MAN1S) $(DESTDIR)$(MAN1DIR)
+ $(Q) install -m 644 $(MANS) $(DESTDIR)$(MAN8DIR)
-uninstall:
- $(E) " UNINSTALL" $(MAN1S)
- $(Q) $(RM) $(addprefix $(DESTDIR)$(MAN1DIR)/,$(MAN1S))
- $(E) " UNINSTALL" $(MAN8S)
- $(Q) $(RM) $(addprefix $(DESTDIR)$(MAN8DIR)/,$(MAN8S))
-
-.PHONY: clean install uninstall
+.PHONY: clean install
diff --git a/Documentation/Makefile.build.txt b/Documentation/Makefile.build.txt
new file mode 100644
index 000000000..9bc35a3f0
--- /dev/null
+++ b/Documentation/Makefile.build.txt
@@ -0,0 +1,194 @@
+Makefile.build(1)
+=================
+:doctype: manpage
+:man source: CRtools
+:man version: 0.0.2
+:man manual: CRtools Manual
+
+NAME
+----
+Makefile.build - a bunch of helpers for simplified Makefiles
+
+
+SYNOPSIS
+--------
+'make' -f scripts/Makefile.build obj=
+
+
+DESCRIPTION
+-----------
+
+This is main build helpers script we use. Basically the idea is to minimize hand
+work and describe Makefiles with somewhat simplified grammar.
+
+The script may work in two modes
+
+ - *Default mode*
+
+ - *Target mode*
+
+Following keywords are reserved and must not be used for anything else --
+'targets', 'deps', 'all-obj', 'incdeps', 'obj-y', 'obj-e', 'asm-y', 'asm-e',
+'file', 'libs-e', '-obj-y', '-obj-e', '-asm-y', '-asm-e',
+'-obj-y-cflags', '-obj-e-cflags', '-asm-y-asmflags', '-asm-e-asmflags',
+'-libs-e'. Where '' is a prefix of the target, will be explained below.
+That said, do not use such names for other purposes as stated here.
+
+OBJ=
+----
+
+Parameter *obj=* states for passing directory where simplified Makefile lays in.
+Note the directory name must not end up with a slash, it is mandatory.
+
+In your simplifiled Makefile you still can refer to it as '$(obj)' variable. If
+you need an ending slash, just type it explicitly as '$(obj)/'.
+
+DEFAULT MODE
+------------
+
+In *default* mode the script builds '$(obj)/built-in.o' relocatable file. To use
+*default* mode do not ever mention '-' and 'targets' variables in a Makefile.
+This done for simplicity, otherwise more complex logic will be needed in the
+script which slows down built procedure.
+
+Thus in *default* mode the following variables may and should be referred
+
+obj-y::
+ Source code C file. Typically refered as *obj-y += 'some-file.o'*.
+ This implies you have real 'some-file.c' in '$(obj)' directory.
+
+obj-e::
+ Same as 'obj-y' but implies that source code file lays in directory
+ other than '$(obj)'. The postfix '-e' came from word 'external'.
+
+obj-ext-src-y::
+ Same as 'obj-y' but implies that source code file lays in directory
+ other than '$(obj)', while compiled object file pushed into '$(obj)' directory.
+ Consider using this variable if you need to compile same source file with
+ different flags.
+
+asm-y::
+ Source code S file. Same as 'obj-y' but for assembly language.
+
+asm-e::
+ Same as 'obj-e' but for assembly language.
+
+lib-e::
+ Some extarnal library the 'built-in.o' should link with.
+
+lib-so::
+ Tells the make engine to build a shared library.
+
+incdeps::
+ A flag which tells the script to generate dependency (that named '*.d'
+ files) for source code C files. To turn this functionality on just
+ type 'incdeps := y' somewhere in your Makefile.
+
+cleanup-y::
+ List of files to be cleaned up when 'clean' target is called.
+
+For example a simplified Makefile may look like
+
+ obj-y += file1.o
+ obj-y += file2.o
+ obj-y += file3.o
+
+ ifneq ($(MAKECMDGOALS),clean)
+ incdeps := y
+ endif
+
+TARGET MODE
+-----------
+
+In *target* mode the script builds all targets declared in a Makefile. Thus the
+final built relocatable files will have a name as '.built-in.o', where ''
+is a name of a target (I will continue using '' to refer the target name).
+The following variables may be used for *target* mode.
+
+targets::
+ This one defines a target name to built.
+
+-obj-y::
+ Same as 'obj-y' but per target.
+
+-obj-y-cflags::
+ Additional compiler flags for this target and object files
+ in '-obj-y'.
+
+-obj-e::
+ Same as 'obj-e' but per target.
+
+-obj-e-cflags::
+ Additional compiler flags for this target and object files
+ in '-obj-e'.
+
+-asm-y::
+ Same as 'asm-y' but per target.
+
+-asm-y-asmflags::
+ Additional compiler flags for this target and object files
+ in '-asm-y'.
+
+-asm-e::
+ Same as 'asm-e' but per target.
+
+-asm-e-asmflags::
+ Additional compiler flags for this target and object files
+ in '-asm-e'.
+
+-libs-e::
+ Same as 'libs-e' but per-target.
+
+There might be a situation where we have several targets and each of them need
+some object file to be linked in. In this case we need to use variables from
+*default* mode. Better to explain with example.
+
+Lets say we need to built two targets 'one' and 'two' (thus 'one.built-in.o' and
+'two.built-in.o' relocatable files will be generated). For 'one' we need to use
+files 'a.o', 'b.o', and for 'two' we need to use 'c.o' and 'd.o'. But both
+targets need functionality from file 'e.o'. To force the script share the 'e.o'
+we describe it as plain 'obj-y'.
+
+ targets += one
+ targets += two
+
+ one-obj-y += a.o
+ one-obj-y += b.o
+
+ two-obj-y += c.o
+ two-obj-y += d.o
+
+ obj-y += e.o
+
+The script will compile all files and link 'one.built-in.o' from files 'one-obj-y'
+plus 'obj-y'. The same applies to the target 'two' ('obj-y' file will be linked
+in as well).
+
+Thus if you refer variables from *default* mode but have 'targets' defined, the
+script will treat such variables as a sign to share the productions at moment
+when targets get linked.
+
+INVISIBLE RULES
+---------------
+
+If the script is used for build procedure then a couple of additional rules are
+generated on the fly. Better to explain with example again.
+
+Lets say we have a Makefile with the following contents
+
+ obj-y += file.o
+
+where $(obj) is a directory named 'dir'. So once we use the script we can
+generate the following files.
+
+make dir/file.o::
+ To compile the file.
+
+make dir/file.s::
+ To generate assembly file from C file.
+
+make dir/file.d::
+ To generate dependency file.
+
+make dir/file.i::
+ To generate C file with preprocessor only.
diff --git a/Documentation/compel.txt b/Documentation/compel.txt
deleted file mode 100644
index 506228f59..000000000
--- a/Documentation/compel.txt
+++ /dev/null
@@ -1,122 +0,0 @@
-COMPEL(1)
-==========
-include::footer.txt[]
-
-NAME
-----
-compel - Execute parasitic code within another process.
-
-SYNOPSIS
---------
-*compel* 'hgen' ['option' ...]
-
-*compel* 'plugins' ['PLUGIN_NAME' ...]
-
-*compel* ['--compat'] 'includes' | 'cflags' | 'ldflags'
-
-*compel* ['--compat'] ['--static'] 'libs'
-
-DESCRIPTION
-------------
-*compel* is a utility to execute arbitrary code, also called parasite code,
-in the context of a foreign process. The parasitic code, once compiled with
-compel flags and packed, can be executed in the context of other tasks. Currently
-there is only one way to load the parasitic blob into victim task using libcompel.a,
-called c-header.
-
-ARGUMENTS
-----------
-
-Positional Arguments
-~~~~~~~~~~~~~~~~~~~~
-
-*hgen*::
- create a header from the .po file, which is the parasite binary.
-
-*plugins*::
- prints the plugins available.
-
-*ldflags*::
- prints the ldflags available to compel during linking of parasite code.
-
-*cflags*::
- prints the compel cflags to be used during compilation of parasitic code.
-
-*includes*::
- prints list of standard include directories.
-
-*libs*::
- prints list of static or dynamic libraries that compel can link with.
-
-OPTIONS
---------
-*-f*, *--file* 'FILE'::
- Path to the binary file, 'FILE', which *compel* must turn into a header
-
-*-o*, *--output* 'FILE'::
- Path to the header file, 'FILE', where compel must write the resulting header.
-
-*-p*, *--prefix* 'NAME'::
- Specify prefix for var names
-
-*-l*, *--log-level* 'NUM'::
- Default log level of compel.
-
-*-h*, *--help*::
- Prints usage and exits.
-
-*-V*, *--version*::
- Prints version number of compel.
-
-SOURCE EXAMPLES
-----------------
-
-Parasitic Code
-~~~~~~~~~~~~~~
-
-*#include *
-
-*int parasite_trap_cmd(int cmd, void *args);* //gets called by compel_run_in_thread()
-
-*int parasite_daemon_cmd(int cmd, void *arg);* // gets called by compel_rpc_call() and compel_rpc_call_sync()
-
-*void parasite_cleanup(void);* //gets called on parasite unload by compel_cure()
-
-Infecting code
-~~~~~~~~~~~~~~
-The parasitic code is compiled and converted to a header using *compel*, and included here.
-
-*#include *
-
-*#include "parasite.h"*
-
-Following steps are performed to infect the victim process:
-
- - stop the task: *int compel_stop_task(int pid);*
- - prepare infection handler: *struct parasite_ctl *compel_prepare(int pid);*
- - execute system call: *int compel_syscall(ctl, int syscall_nr, long *ret, int arg ...);*
- - infect victim: *int compel_infect(ctl, nr_thread, size_of_args_area);*
- - cure the victim: *int compel_cure(ctl);* //ctl pointer is freed by this call
- - Resume victim: *int compel_resume_task(pid, orig_state, state)* or
- *int compel_resume_task_sig(pid, orig_state, state, stop_signo).*
- //compel_resume_task_sig() could be used in case when victim is in stopped state.
- stop_signo could be read by calling compel_parse_stop_signo().
-
-*ctl* must be configured with blob information by calling *PREFIX_setup_c_header()*, with ctl as its argument.
-*PREFIX* is the argument given to *-p* when calling hgen, else it is deduced from file name.
-
-
-EXAMPLES
----------
-To generate a header file(.h) from a parasite binary file(.po) use:
-
-----------
- compel hgen -f parasite.po -o parasite.h
-----------
-
-'parasite.po' file is obtained by compiling the parasite source with compel flags and
-linking it with the compel plugins.
-
-AUTHOR
-------
-The CRIU team.
diff --git a/Documentation/crit.txt b/Documentation/crit.txt
deleted file mode 100644
index 7fcf42e80..000000000
--- a/Documentation/crit.txt
+++ /dev/null
@@ -1,96 +0,0 @@
-CRIT(1)
-=======
-include::footer.txt[]
-
-NAME
-----
-crit - CRiu Image Tool
-
-SYNOPSIS
---------
-*crit* 'decode' [-h] [-i IN] [-o OUT] [--pretty]
-
-*crit* 'encode' [-h] [-i IN] [-o OUT]
-
-*crit* 'info' [-h] in
-
-*crit* 'x' [-h] dir {ps,fds,mems}
-
-*crit* 'show' [-h] in
-
-*crit* 'compress' [-h] [--in-place] [--acceleration N] dir
-
-*crit* 'decompress' [-h] [--in-place] dir
-
-DESCRIPTION
------------
-*crit* is a feature-rich replacement for existing *criu* show.
-
-ARGUMENTS
----------
-
-Positional Arguments
-~~~~~~~~~~~~~~~~~~~~
-
-*decode*::
- convert *criu* image from binary type JSON
-
-*encode*::
- convert *criu* image from JSON type to binary
-
-*info*::
- show info about image
-
-*x*::
- explore image directory
-
-*show*::
- convert *criu* image from binary to human-readable JSON
-
-*compress*::
- compress memory page images in a checkpoint directory using CRIU's
- per-page LZ4 format. The command rewrites *pages-*.img*,
- *pagemap-*.img*, and *inventory.img*. Pages that are zero-filled or
- do not meet CRIU's compression threshold are stored without an LZ4
- payload. By default, the original files are preserved with a *.bak*
- suffix; use *--in-place* to skip these backup files. The inventory
- is updated to record compression metadata and the compressed-image
- version. Existing *.bak* files are never overwritten.
-
-*decompress*::
- decompress memory page images in a checkpoint directory back to
- uncompressed page payloads. The command removes compression metadata
- from the pagemap and inventory images. When the directory has no parent
- reference, the command also restores the normal image version. It retains
- the compressed-image version for incremental checkpoints because a parent
- may still contain compressed payloads. By default, rewritten files are
- preserved with a *.bak* suffix; use *--in-place* to skip these backup
- files. Existing *.bak* files are never overwritten.
-
-Both transformations preserve the ownership, permissions, ACLs, security
-labels, and other extended attributes of rewritten images. All output is
-staged and synchronized before it is installed. If a write, rename, *SIGHUP*,
-*SIGINT*, or *SIGTERM* interrupts the commit, CRIT restores the complete
-original image set instead of leaving a partially transformed set.
-
-Optional Arguments
-~~~~~~~~~~~~~~~~~~
-
-*-h*, *--help*::
- Print some help and exit
-
-*--in-place*::
- Rewrite files without creating *.bak* backup files. This option is
- accepted by *compress* and *decompress*.
-
-*--acceleration* 'N'::
- Set the LZ4 acceleration level used by *compress*. Higher values
- trade compression ratio for speed.
-
-SEE ALSO
---------
-criu(8)
-
-AUTHOR
-------
-The CRIU team
diff --git a/Documentation/criu-amdgpu-plugin.txt b/Documentation/criu-amdgpu-plugin.txt
deleted file mode 100644
index fe76fc3bc..000000000
--- a/Documentation/criu-amdgpu-plugin.txt
+++ /dev/null
@@ -1,114 +0,0 @@
-ROCM Support(1)
-===============
-
-NAME
-----
-criu-amdgpu-plugin - A plugin extension to CRIU to support checkpoint/restore in
-userspace for AMD GPUs.
-
-
-CURRENT SUPPORT
----------------
-Single and Multi GPU systems (Gfx9)
-Checkpoint / Restore on different system
-Checkpoint / Restore inside a docker container
-Pytorch
-Tensorflow
-Using CRIU Image Streamer
-Parallel Restore
-
-DESCRIPTION
------------
-Though *criu* is a great tool for checkpointing and restoring running
-applications, it has certain limitations such as it cannot handle
-applications that have device files open. In order to support *ROCm* based
-workloads with *criu* we need to augment criu's core functionality with a
-plugin based extension mechanism. *criu-amdgpu-plugin* provides the necessary support
-to criu to allow Checkpoint / Restore with ROCm.
-
-
-Dependencies
-------------
-*amdkfd support*::
- In order to snapshot the *VRAM* and other *GPU* device states, we require
- an updated version of amdkfd(amdgpu) driver.
-
-OPTIONS
--------
-Optional parameters can be passed in as environment variables before
-executing criu command.
-
-*KFD_FW_VER_CHECK*::
- Enable or disable firmware version check.
- If enabled, firmware version on restored gpu needs to be greater than or
- equal firmware version on checkpointed GPU. Default:Enabled
-
- E.g:
- KFD_FW_VER_CHECK=0
-
-*KFD_SDMA_FW_VER_CHECK*::
- Enable or disable SDMA firmware version check.
- If enabled, SDMA firmware version on restored gpu needs to be greater than or
- equal firmware version on checkpointed GPU. Default:Enabled
-
- E.g:
- KFD_SDMA_FW_VER_CHECK=0
-
-*KFD_CACHES_COUNT_CHECK*::
- Enable or disable caches count check. If enabled, the caches count on
- restored GPU needs to be greater than or equal caches count on checkpointed
- GPU. Default:Enabled
-
- E.g:
- KFD_CACHES_COUNT_CHECK=0
-
-*KFD_NUM_GWS_CHECK*::
- Enable or disable num_gws check. If enabled, the num_gws on
- restored GPU needs to be greater than or equal num_gws on checkpointed
- GPU. Default:Enabled
-
- E.g:
- KFD_NUM_GWS_CHECK=0
-
-*KFD_VRAM_SIZE_CHECK*::
- Enable or disable VRAM size check. If enabled, the VRAM size on
- restored GPU needs to be greater than or equal VRAM size on checkpointed
- GPU. Default:Enabled
-
- E.g:
- KFD_VRAM_SIZE_CHECK=0
-
-*KFD_NUMA_CHECK*::
- Enable or disable NUMA CPU region check. If enabled, the plugin will restore
- GPUs that belong to one CPU NUMA region to the same CPU NUMA region.
- Default:Enabled
-
- E.g:
- KFD_NUMA_CHECK=1
-
-*KFD_CAPABILITY_CHECK*::
- Enable or disable capability check. If enabled, the capability on
- restored GPU needs to be equal to the capability on the checkpointed GPU.
- Default:Enabled
-
- E.g:
- KFD_CAPABILITY_CHECK=1
-
-*KFD_MAX_BUFFER_SIZE*::
- On some systems, VRAM sizes may exceed RAM sizes, and so buffers for dumping
- and restoring VRAM may be unable to fit. Set to a nonzero value (in bytes)
- to set a limit on the plugin's memory usage.
- Default:0 (Disabled)
-
- E.g:
- KFD_MAX_BUFFER_SIZE="2G"
-
-
-AUTHOR
-------
-The AMDKFD team.
-
-
-COPYRIGHT
----------
-Copyright \(C) 2020-2021, Advanced Micro Devices, Inc. (AMD)
diff --git a/Documentation/criu-ns.txt b/Documentation/criu-ns.txt
deleted file mode 100644
index c6594a9bc..000000000
--- a/Documentation/criu-ns.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-CRIU-NS(1)
-==========
-include::footer.txt[]
-
-NAME
-----
-criu-ns - run criu in different namespaces
-
-SYNOPSIS
---------
-*criu-ns* 'dump' -t PID []
-
-*criu-ns* 'pre-dump' -t PID []
-
-*criu-ns* 'restore' []
-
-*criu-ns* 'check' []
-
-DESCRIPTION
------------
-The *criu-ns* command executes 'criu' in a new PID and mount namespace.
-The purpose of this wrapper script is to enable restoring a process tree
-that might require a specific PID that is already used on the system;
-so called "PID mismatch" problem.
-
-SEE ALSO
---------
-nsenter(1) namespaces(7) criu(8)
-
-AUTHOR
-------
-The CRIU team
diff --git a/Documentation/criu.txt b/Documentation/criu.txt
index 38df6c98f..f9b6513dd 100644
--- a/Documentation/criu.txt
+++ b/Documentation/criu.txt
@@ -1,1074 +1,270 @@
CRIU(8)
=======
-include::footer.txt[]
+:doctype: manpage
+:man source: criu
+:man version: 0.0.2
+:man manual: CRIU Manual
NAME
----
criu - checkpoint/restore in userspace
-
SYNOPSIS
--------
-*criu* 'command' ['option' ...]
-
+*criu* 'command' ['options']
DESCRIPTION
-----------
-*criu* is a tool for checkpointing and restoring running applications.
-It does this by saving their state as a collection of files (see the *dump*
-command) and creating equivalent processes from those files (see the *restore*
-command). The restore operation can be performed at a later time,
-on a different system, or both.
+*criu* is command line utility to steer checkpoint and restore procedure.
+The 'command' can be one of the following:
+
+*pre-dump*::
+Launch that named pre-dump procedure, where *criu* does snapshot of
+memory changes since previous pre-dump. Also *criu* forms fsnotify
+cache which speedup *restore* procedure.
+
+*dump*::
+Initiate checkpoint procedure.
+
+*restore*::
+Restore previously checkpointed processes.
+
+*show*::
+Decode own binary dump files and show their contents in human-readable form.
+
+*check*::
+Test whether the kernel support is up-to-date.
+
+*page-server*::
+Launch a page server.
+
+*exec*::
+Execute a system call from other task\'s context.
+
+*service*::
+Start RPC service.
+
+*dedup*::
+Starts pagemap data deduplication procedure, where *criu* scans over all
+pagemap files and tries to minimalize the number of pagemap entries by
+obtaining the references from a parent pagemap image.
+
+*cpuinfo* *dump*::
+Writes information about currently running CPU and its features into an image.
+
+*cpuinfo* *check*::
+Reads information about CPU from an image file and checks if it is compatible
+with currently running CPU.
OPTIONS
-------
-
-Most of the long flags can be
-prefixed with *no-* to negate the option (example: *--display-stats*
-and *--no-display-stats*).
-
-Common options
-~~~~~~~~~~~~~~
-Common options are applicable to any 'command'.
-
-*-v*[*v*...], *--verbosity*::
- Increase verbosity up from the default level. In case of short option,
- multiple *v* can be used, each increasing verbosity by one.
-
-**-v**__num__, **--verbosity=**__num__::
- Set verbosity level to _num_. The higher the level, the more output
- is produced.
- +
-The following levels are available:
- * *-v0*
- no output;
- * *-v1*
- only errors;
- * *-v2*
- above plus warnings (this is the default level);
- * *-v3*
- above plus information messages and timestamps;
- * *-v4*
- above plus lots of debug.
-
-*--config* 'file'::
- Pass a specific configuration file to criu.
-
-*--no-default-config*::
- Disable parsing of default configuration files.
-
-*--pidfile* 'file'::
- Write root task, service or page-server pid into a 'file'.
-
-*-o*, *--log-file* 'file'::
- Write logging messages to a 'file'.
-
-*--display-stats*::
- During dump, as well as during restore, *criu* collects some statistics,
- like the time required to dump or restore the process, or the
- number of pages dumped or restored. This information is always
- saved to the *stats-dump* and *stats-restore* files, and can
- be shown using *crit*(1). The option *--display-stats*
- prints out this information on the console at the end
- of a dump or restore operation.
+*-c*::
+ In case of *show* command the dumped pages content will be shown in hex format.
*-D*, *--images-dir* 'path'::
- Use 'path' as a base directory where to look for sets of image files.
-
-*--stream*::
- dump/restore images using criu-image-streamer.
- See https://github.com/checkpoint-restore/criu-image-streamer for detailed
- usage.
-
-*--prev-images-dir* 'path'::
- Use 'path' as a parent directory where to look for sets of image files.
- This option makes sense in case of incremental dumps.
+ Use path 'path' as a base directory where to look for dump files set. This
+ commands applies to any 'command'.
*-W*, *--work-dir* 'dir'::
Use directory 'dir' for putting logs, pidfiles and statistics. If not
specified, 'path' from *-D* option is taken.
-*--close* 'fd'::
- Close file descriptor 'fd' before performing any actions.
+*-s*, *--leave-stopped*::
+ Leave tasks in stopped state after checkpoint instead of killing them.
-*-L*, *--libdir* 'path'::
- Path to plugins directory.
+*-R*, *--leave-running*::
+ Leave tasks in running state after checkpoint instead of killing them. This
+ option is pretty dangerous and should be used if and only if you understand
+ what you are doing.
+ If task is about to run after been checkpointed it can modify TCP connections,
+ delete files and do other dangerous actions. So that *criu* itself can not
+ guarantee that the next *restore* action will not fail. Most likely if a user
+ starts *criu* with this option passed at least the file system snapshot must be
+ done with help of 'post-dump' script.
+ In other words, do not use it until really needed.
-*--enable-fs* ['fs'[,'fs'...]]::
- Specify a comma-separated list of filesystem names that should
- be auto-detected. The value 'all' enables auto-detection for
- all filesystems.
-+
-Note: This option is not safe, use at your own risk.
-Auto-detecting a filesystem mount assumes that the mountpoint can
-be restored with *mount(src, mountpoint, flags, options)*. When used,
-*dump* is expected to always succeed if a mountpoint is to be
-auto-detected, however *restore* may fail (or do something wrong)
-if the assumption for restore logic is incorrect. This option is
-not compatible with *--external* *dev*.
+*--cpu-cap* [,'cap']::
+ When restore process require 'cap' CPU capability to be present. To inverse
+ capability prefix it with '^'.
-*--action-script* 'script'::
- Add an external action script to be executed at certain stages.
- The environment variable *CRTOOLS_SCRIPT_ACTION* is available
- to the script to find out which action is being executed, and
- its value can be one of the following:
- *pre-dump*:::
- run prior to beginning a *dump*
+ - 'all'. Require all capabilities. This is *default* mode if *--cpu-cap*
+ is passed without arguments. Most safe mode.
- *post-dump*:::
- run upon *dump* completion
+ - 'cpu'. Require the CPU to have all capabilities match. On *dump* the
+ capabilities are writen into image file and on *restore* they
+ are validated to match ones present on runtime CPU.
- *pre-restore*:::
- run prior to beginning a *restore*
+ - 'fpu'. Requre the CPU to have comaptible FPU. For example the process
+ might be dumped with xsave capability but attempted to restore
+ without it present on target CPU. In such case we refuse to
+ procceed. This is *default* mode if *--cpu-cap* is not present
+ in command line.
- *post-restore*:::
- run upon *restore* completion
+ - 'ins' Only require CPU compatibility on instructions level. On *dump*
+ all capabilities are writen into image file and on *restore*
+ only subset related to CPU instructions tested if target CPU
+ supports them. Unlike 'cpu' mode the target CPU may have more
+ features than ones present in image file.
- *pre-resume*:::
- run when all processes and resources are
- restored but tasks are stopped waiting for
- final kick to run. Must not fail.
+ - 'none'. Ignore capabilities. Most dangerous mode. The behaviour is
+ implementation dependent. Try to not use it until really
+ required. One possible need of using this option is when
+ *--cpu-cap*='cpu' has been passed on *dump* then images are
+ migrated to a less capable processor and one need to *restore*
+ this application, by default *criu* will refuse to proceed without
+ relaxing capability with *--cpu-cap*='none' parameter.
- *post-resume*:::
- called at the very end, when everything is
- restored and processes were resumed
+*-f*, *--file* 'file'::
+ This option is valid for the *show* command only and allows one to see the
+ content of the 'file' specified.
- *network-lock*:::
- run to lock network in a target network namespace
-
- *network-unlock*:::
- run to unlock network in a target network namespace
-
- *setup-namespaces*:::
- run once root task has just been created
- with required namespaces. Note it is an early stage
- of restore, when nothing is restored yet, except for
- namespaces themselves
-
- *post-setup-namespaces*:::
- called after the namespaces are configured
-
- *orphan-pts-master*:::
- called after master pty is opened and unlocked. This
- hook can be used only in the RPC mode, and the
- notification message contains a file descriptor for
- the master pty
-
- *query-ext-files*:::
- called after the process tree is stopped and network is locked.
- This hook is used only in the RPC mode. The notification reply
- contains file ids to be added to external file list (may be empty).
-
-*--unprivileged*::
- This option tells *criu* to accept the limitations when running
- as non-root. Running as non-root requires *criu* at least to have
- *CAP_SYS_ADMIN* or *CAP_CHECKPOINT_RESTORE*. For details about running
- *criu* as non-root please consult the *NON-ROOT* section.
-
-*-V*, *--version*::
- Print program version and exit.
-
-*-h*, *--help*::
- Print some help and exit.
-
-*pre-dump*
-~~~~~~~~~~
-Performs the pre-dump procedure, during which *criu* creates a snapshot of
-memory changes since the previous *pre-dump*. Note that during this
-*criu* also creates the fsnotify cache which speeds up the *restore*
-procedure. *pre-dump* requires at least *-t* option (see *dump* below).
-In addition, *page-server* options may be specified.
-
-*--track-mem*::
- Turn on memory changes tracker in the kernel. If the option is
- not passed the memory tracker get turned on implicitly.
-
-*--pre-dump-mode*='mode'::
- There are two 'mode' to operate pre-dump algorithm. The 'splice' mode
- is parasite based, whereas 'read' mode is based on process_vm_readv
- syscall. The 'read' mode incurs reduced frozen time and reduced
- memory pressure as compared to 'splice' mode. Default is 'splice' mode.
-
-*dump*
-~~~~~~
-Performs a checkpoint procedure.
+*-x*, *--ext-unix-sk*::
+ Dump external unix sockets.
*-t*, *--tree* 'pid'::
Checkpoint the whole process tree starting from 'pid'.
-*-R*, *--leave-running*::
- Leave tasks in running state after checkpoint, instead of killing. This
- option is pretty dangerous and should be used only if you understand
- what you are doing.
-+
-Note if task is about to run after been checkpointed, it can modify
-TCP connections, delete files and do other dangerous actions. Therefore,
-*criu* can not guarantee that the next *restore* action will succeed.
-Most likely if this option is used, at least the file system snapshot
-must be made with the help of *post-dump* action script.
-+
-In other words, do not use it unless really needed.
+*-d*, *--restore-detached*::
+ Detach *criu* itself once restore is complete.
-*-s*, *--leave-stopped*::
- Leave tasks in stopped state after checkpoint, instead of killing.
+*-n*, *--namespaces* 'ns'[,'ns'...]::
+ Checkpoint namespaces. Namespaces must be separated by comma.
+ Currently supported namespaces: *uts*, *ipc*, *mnt*, *pid*, *net*.
-*--external* __type__**[**__id__**]:**__value__::
- Dump an instance of an external resource. The generic syntax is
- 'type' of resource, followed by resource 'id' (enclosed in literal
- square brackets), and optional 'value' (prepended by a literal colon).
- The following resource types are currently supported: *mnt*, *dev*,
- *file*, *tty*, *unix*. Syntax depends on type.
- Note to restore external resources, either *--external* or *--inherit-fd*
- is used, depending on resource type.
-
-*--external* **mnt[**__mountpoint__**]:**__name__::
- Dump an external bind mount referenced by 'mountpoint', saving it
- to image under the identifier 'name'.
-
-*--external* **mnt[]:**__flags__::
- Dump all external bind mounts, autodetecting those. Optional 'flags'
- can contain *m* to also dump external master mounts, *s* to also
- dump external shared mounts (default behavior is to abort dumping
- if such mounts are found). If 'flags' are not provided, colon
- is optional.
-
-*--external* **dev[**__major__**/**__minor__**]:**__name__::
- Allow to dump a mount namespace having a real block device mounted.
- A block device is identified by its 'major' and 'minor' numbers,
- and *criu* saves its information to image under the identifier 'name'.
-
-*--external* **file[**__mnt_id__**:**__inode__**]**::
- Dump an external file, i.e. an opened file that is can not be resolved
- from the current mount namespace, which can not be dumped without using
- this option. The file is identified by 'mnt_id' (a field obtained from
- **/proc/**__pid__**/fdinfo/**__N__) and 'inode' (as returned by
- *stat*(2)).
-
-*--external* **tty[**__rdev__**:**__dev__**]**::
- Dump an external TTY, identified by *st_rdev* and *st_dev* fields
- returned by *stat*(2).
-
-*--external* **unix[**__id__**]**::
- Tell *criu* that one end of a pair of UNIX sockets (created by
- *socketpair*(2)) with the given _id_ is OK to be disconnected.
-
-*--external* **net[**__inode__**]:**__name__::
- Mark a network namespace as external and do not include it in the
- checkpoint. The label 'name' can be used with *--inherit-fd* during
- restore to specify a file descriptor to a preconfigured network
- namespace.
-
-*--external* **pid[**__inode__**]:**__name__::
- Mark a PID namespace as external. This can be later used to restore
- a process into an existing PID namespace. The label 'name' can be
- used to assign another PID namespace during restore with the help
- of *--inherit-fd*.
-
-*--freeze-cgroup*::
- Use cgroup freezer to collect processes.
-
-*--manage-cgroups*::
- Collect cgroups into the image thus they gonna be restored then.
- Without this option, *criu* will not save cgroups configuration
- associated with a task.
-
-*--cgroup-props* 'spec'::
- Specify controllers and their properties to be saved into the
- image file. *criu* predefines specifications for common controllers,
- but since the kernel can add new controllers and modify their
- properties, there should be a way to specify ones matched the kernel.
-+
-'spec' argument describes the controller and properties specification in
-a simplified YAML form:
-+
-----------
-"c1":
- - "strategy": "merge"
- - "properties": ["a", "b"]
-"c2":
- - "strategy": "replace"
- - "properties": ["c", "d"]
-----------
-+
-where 'c1' and 'c2' are controllers names, and 'a', 'b', 'c', 'd' are
-their properties.
-+
-Note the format: double quotes, spaces and new lines are required.
-The 'strategy' specifies what to do if a controller specified already
-exists as a built-in one: *criu* can either *merge* or *replace* such.
-+
-For example, the command line for the above example should look like this:
-+
-----------
---cgroup-props "\"c1\":\n - \"strategy\": \"merge\"\n - \"properties\": [\"a\", \"b\"]\n \"c2\":\n - \"strategy\": \"replace\"\n - \"properties\": [\"c\", \"d\"]"
-----------
-
-*--cgroup-props-file* 'file'::
- Same as *--cgroup-props*, except the specification is read from
- the 'file'.
-
-*--cgroup-dump-controller* 'name'::
- Dump a controller with 'name' only, skipping anything else that was
- discovered automatically (usually via */proc*). This option is
- useful when one needs *criu* to skip some controllers.
-
-*--cgroup-yard* 'path'::
- Instead of trying to mount cgroups in CRIU, provide a path to a directory
- with already created cgroup yard. Useful if you don't want to grant
- CAP_SYS_ADMIN to CRIU. For every cgroup mount there should be exactly one
- directory. If there is only one controller in this mount, the dir's name
- should be just the name of the controller. If there are multiple controllers
- comounted, the directory name should have them be separated by a comma.
-+
-For example, if */proc/cgroups* looks like this:
-+
-----------
-#subsys_name hierarchy num_cgroups enabled
-cpu 1 1 1
-devices 2 2 1
-freezer 2 2 1
-----------
-+
-then you can create the cgroup yard by the following commands:
-+
-----------
-mkdir private_yard
-cd private_yard
-mkdir cpu
-mount -t cgroup -o cpu none cpu
-mkdir devices,freezer
-mount -t cgroup -o devices,freezer none devices,freezer
-----------
-
-*--tcp-established*::
- Checkpoint established TCP connections.
-
-*--tcp-close*::
- Don't dump the state of, or block, established tcp connections
- (including the connection is once established but now closed).
- This is useful when tcp connections are not going to be restored.
-
-*--skip-in-flight*::
- This option skips in-flight TCP connections. If any TCP connections
- that are not yet completely established are found, *criu* ignores
- these connections, rather than errors out.
- The TCP stack on the client side is expected to handle the
- re-connect gracefully.
+*-r*, *--root* 'path'::
+ Change the root filesystem (when run in mount namespace).
*--evasive-devices*::
Use any path to a device file if the original one is inaccessible.
-*--page-server*::
- Send pages to a page server (see the *page-server* command).
+*--veth-pair* 'IN'*=*'OUT'::
+ Correspondence between outside and inside names of veth devices.
-*--force-irmap*::
- Force resolving names for inotify and fsnotify watches.
+*-M*, *--ext-mount-map* 'KEY'*:*'VAL'::
+ Setup mapping for external mounts.
-*--auto-dedup*::
- Deduplicate "old" data in pages images of previous *dump*. This option
- implies incremental *dump* mode (see the *pre-dump* command).
+ On dump, KEY is a mountpoint inside container and corresponding VAL
+ is a string that will be written into the image as mountpoint's root
+ value
-*-l*, *--file-locks*::
- Dump file locks. It is necessary to make sure that all file lock users
- are taken into dump, so it is only safe to use this for enclosed containers
- where locks are not held by any processes outside of dumped process tree.
+ On restore KEY is the value from the image (VAL from dump) and the
+ VAL is the path on host that will be bind-mounted into container
+ (to the mountpoint path from image)
+
+*--action-script* 'SCRIPT'::
+ Add an external action script.
+ The environment variable *CRTOOLS_SCRIPT_ACTION* contains one of the
+ actions:
+ * *network-lock*
+ lock network in a target network namespace
+
+ * *network-unlock*
+ unlock network in a target network namespace
*--link-remap*::
- Allows to link unlinked files back, if possible (modifies filesystem
- during *restore*).
+ Allow to link unlinked files back when possible (modifies FS
+ till restore).
-*-c, --compress*::
- Enable LZ4 per-page compression of memory pages during *dump*.
- Each system page is compressed as an independent LZ4 block.
- Zero-filled pages are detected and stored with no data; pages that
- do not compress well are stored raw to avoid decompression overhead
- on *restore*. The compression choice made at *dump* time is recorded
- in the inventory image and applied automatically on *restore*.
- Compatible with *--page-server* when *--tls* is not used, *--stream*,
- *--lazy-pages*, iterative checkpointing, *--auto-dedup*, and the *dedup*
- command.
+*-o*, *--log-file* 'file'::
+ Write logging messages to 'file'.
-*--compress-region* 'size'::
- Enable LZ4 region compression of memory pages during *dump*.
- A run of consecutive pages totalling 'size' bytes is compressed as
- a single LZ4 block, which yields a better ratio and faster dump
- on heap-shaped data than per-page compression at the cost of
- slightly slower restore on partial reads. 'size' accepts K/M/G
- suffixes (e.g. *256K*, *1M*); it must be a multiple of the system
- page size and at most 4M. Mutually exclusive with *--compress*.
- Currently supports only the local image path (no
- *--page-server* / *--stream*). Hole-punching deduplication is
- skipped for compressed images since compressed pages are
- variably-sized.
-
-*--compress-acceleration* 'N'::
- Set the LZ4 acceleration level for page compression (1 to 65537).
- Controls how thoroughly the compressor searches for matching byte
- sequences. The default value of 1 gives the best compression
- ratio. Higher values skip more match candidates, resulting in
- faster compression but larger output. Decompression speed is not
- affected. Implies *--compress* unless *--compress-region* is set.
-
-*--decompress-threads* 'N'::
- Set worker concurrency for LZ4 decoding and eligible large zero fills,
- including the calling restore thread. The default value of 1 keeps each
- LZ4 decode serial and handles zero blocks without a worker pool;
- independent private, shmem, and memfd requests can still run concurrently.
- A value of 0 lets CRIU choose the concurrency for each batch. CRIU starts
- with the CPUs allowed by its affinity mask, then limits the width by the
- number and decoded size of the blocks in that batch and by the restore-wide
- CPU budget. Small batches and zero runs with fewer than two blocks remain
- serial. Values above 1 set an upper bound on aggregate worker concurrency
- instead of using the automatic CPU count. Explicit requests above the
- available CPU count are reduced with a warning. The accepted range is 0
- through 1024; this validation ceiling does not determine the automatic
- concurrency.
- Encoded staging memory is bounded independently of the thread count.
- Restore keeps at most two active 32 MiB encoded input buffers. When a
- local reader can reserve a second buffer without waiting, its calling thread
- reads the next payload while pool workers decode the current one. If
- another restore request already owns the second buffer, reading remains
- synchronous. The option can also be set through a configuration file
- (*decompress-threads 0*).
-
-*--timeout* 'number'::
- Set a time limit in seconds for collecting tasks during the
- dump operation. The timeout is 10 seconds by default.
-
-*--ghost-limit* 'size'::
- Set the maximum size of deleted file to be carried inside image.
- By default, up to 1M file is allowed. Using this
- option allows to not put big deleted files inside images. Argument
- 'size' may be postfixed with a *K*, *M* or *G*, which stands for kilo-,
- mega, and gigabytes, accordingly.
-
-*--ghost-fiemap*::
- Enable an optimization based on fiemap ioctl that can reduce the
- number of system calls used when checkpointing highly sparse ghost
- files. This option is enabled by default, and it can be disabled
- with *--no-ghost-fiemap*. An automatic fallback to SEEK_HOLE/SEEK_DATA
- is used when fiemap is not supported.
-
-*-j*, *--shell-job*::
- Allow one to dump shell jobs. This implies the restored task will
- inherit session and process group ID from the *criu* itself.
- This option also allows to migrate a single external tty connection,
- to migrate applications like *top*. If used with *dump* command,
- it must be specified with *restore* as well.
-
-*--cpu-cap* ['cap'[,'cap'...]]::
- Specify CPU capabilities to write to an image file. The argument is a
- comma-separated list of:
-+
- - *none* to ignore capabilities at all; the image will not be produced
- on dump, neither any check performed on restore;
- - *fpu* to check if FPU module is compatible;
- - *ins* to check if CPU supports all instructions required;
- - *cpu* to check if CPU capabilities are exactly matching;
- - *all* for all above set.
-
-+
-By default the option is set to *fpu* and *ins*.
-
-*--cgroup-root* ['controller':]/'newroot'::
- Change the root for the controller that will be dumped. By default, *criu*
- simply dumps everything below where any of the tasks live. However, if a
- container moves all of its tasks into a cgroup directory below the container
- engine's default directory for tasks, permissions will not be preserved on
- the upper directories with no tasks in them, which may cause problems.
-
-*--lazy-pages*::
- Perform the dump procedure without writing memory pages into the
- image files and prepare to service page requests over the
- network. When *dump* runs in this mode it presumes that
- *lazy-pages* daemon will connect to it and fetch memory pages to
- lazily inject them into the restored process address space. This
- option is intended for post-copy (lazy) migration and should be
- used in conjunction with *restore* with appropriate options.
-
-*--file-validation* ['mode']::
- Set the method to be used to validate open files. Validation is done
- to ensure that the version of the file being restored is the same
- version when it was dumped.
-+
-The 'mode' may be one of the following:
-
- *filesize*:::
- To explicitly use only the file size check all the time.
- This is the fastest and least intensive check.
-
- *buildid*:::
- To validate ELF files with their build-ID. If the
- build-ID cannot be obtained, 'chksm-first' method will be
- used. This is the default if mode is unspecified.
-
-*--image-io-mode* ['mode']::
- Set the I/O mode used when writing and reading the memory pages
- image. It controls whether the host page cache is used. The mode is
- selected independently for *dump* and *restore*; the image bytes are
- identical either way.
-+
-The 'mode' may be one of the following:
-
- *writeback*:::
- Buffered I/O via the host page cache. This is the default.
-
- *direct*:::
- Use O_DIRECT to bypass the host page cache. If the
- filesystem does not support O_DIRECT, *criu* falls back to
- buffered I/O.
-
-*--network-lock* ['mode']::
- Set the method to be used for network locking/unlocking. Locking is done
- to ensure that tcp packets are dropped between dump and restore. This is
- done to avoid the kernel sending RST when a packet arrives destined for
- the dumped process.
-+
-The 'mode' may be one of the following:
-
- *iptables*::: Use iptables rules to drop the packets.
- This is the default if 'mode' is not specified.
-
- *nftables*::: Use nftables rules to drop the packets.
-
- *skip*::: Don't lock the network. If *--tcp-close* is not used, the network
- must be locked externally to allow CRIU to dump TCP connections.
-
-*--allow-uprobes*::
- Allow dumping when uprobes vma is present. When used on dump, this option is
- required on restore as well.
-
- A uprobes vma is automatically created by the kernel once a uprobe is
- triggered. This mapping is not removed even once the uprobe is deleted. So,
- even if a process once had uprobes attached to it, and they're removed by
- the time the process is dumped, this option is still required because criu
- has no way of knowing whether there are active uprobes or not.
-
- When using this option on restore, make sure the uprobes (if any) active on
- the dumped processes are still active. Otherwise, when execution reaches
- a uprobe'd location in any of the restored processes, that process will be
- sent a SIGTRAP.
-
- As an example, say a uprobe is set at function foo in the executable of the
- process p_bar. Whenever execution in p_bar reaches function foo, the uprobe
- is triggered. If the uprobe has been triggered at least once, then the kernel
- will have created the uprobes vma. To dump p_bar, this option is
- necessary. After dumping, say the uprobe is deleted. Now, on restoring with
- this option, once execution reaches function foo, SIGTRAP will be sent to
- the restored p_bar. Unless it has a signal handler installed for SIGTRAP,
- it will be terminated and core dumped.
-
-*restore*
-~~~~~~~~~
-Restores previously checkpointed processes.
-
-*--inherit-fd* **fd[**__N__**]:**__resource__::
- Inherit a file descriptor. This option lets *criu* use an already opened
- file descriptor 'N' for restoring a file identified by 'resource'.
- This option can be used to restore an external resource dumped
- with the help of *--external* *file*, *tty*, *pid* and *unix* options.
-+
-The 'resource' argument can be one of the following:
-+
- - **tty[**__rdev__**:**__dev__**]**
- - **pipe:[**__inode__**]**
- - **socket:[**__inode__*]*
- - **file[**__mnt_id__**:**__inode__**]**
- - 'path/to/file'
-
-+
-Note that square brackets used in this option arguments are literals and
-usually need to be escaped from shell.
-
-*-d*, *--restore-detached*::
- Detach *criu* itself once restore is complete.
-
-*-s*, *--leave-stopped*::
- Leave tasks in stopped state after restore (rather than resuming
- their execution).
-
-*-S*, *--restore-sibling*::
- Restore root task as a sibling (makes sense only with
- *--restore-detached*).
+*-v*['num'|*v*...]::
+ Set logging level to 'num'. The higer the level, the more output
+ is produced. Either numeric values or multiple *v* can be used.
+ The following levels are available:
+ * *-v1*, *-v* - only messages and errors;
+ * *-v2*, *-vv* - also warnings (default level);
+ * *-v3*, *-vvv* - also information messages and timestamps;
+ * *-v4*, *-vvvv* - lots of debug.
*--log-pid*::
Write separate logging files per each pid.
-*-r*, *--root* 'path'::
- Change the root filesystem to 'path' (when run in a mount namespace).
- This option is required to restore a mount namespace. The directory
- 'path' must be a mount point and its parent must not be overmounted.
-
-*--external* __type__**[**__id__**]:**__value__::
- Restore an instance of an external resource. The generic syntax is
- 'type' of resource, followed by resource 'id' (enclosed in literal
- square brackets), and optional 'value' (prepended by a literal colon).
- The following resource types are currently supported: *mnt*, *dev*,
- *veth*, *macvlan*. Syntax depends on type. Note to restore external
- resources dealing with opened file descriptors (such as dumped with
- the help of *--external* *file*, *tty*, and *unix* options), option
- *--inherit-fd* should be used.
-
-*--external* **mnt[**__name__**]:**__mountpoint__::
- Restore an external bind mount referenced in the image by 'name',
- bind-mounting it from the host 'mountpoint' to a proper mount point.
-
-*--external mnt[]*::
- Restore all external bind mounts (dumped with the help of
- *--external mnt[]* auto-detection).
-
-*--external* **dev[**__name__**]:**__/dev/path__::
- Restore an external mount device, identified in the image by 'name',
- using the existing block device '/dev/path'.
-
-*--external* **veth[**__inner_dev__**]:**__outer_dev__**@**__bridge__::
- Set the outer VETH device name (corresponding to 'inner_dev' being
- restored) to 'outer_dev'. If optional **@**_bridge_ is specified,
- 'outer_dev' is added to that bridge. If the option is not used,
- 'outer_dev' will be autogenerated by the kernel.
-
-*--external* **macvlan[**__inner_dev__**]:**__outer_dev__::
- When restoring an image that have a MacVLAN device in it, this option
- must be used to specify to which 'outer_dev' (an existing network device
- in CRIU namespace) the restored 'inner_dev' should be bound to.
-
-*-J*, *--join-ns* **NS**:{**PID**|**NS_FILE**}[,**EXTRA_OPTS**]::
- Restore process tree inside an existing namespace. The namespace can
- be specified in 'PID' or 'NS_FILE' path format (example:
- *--join-ns net:12345* or *--join-ns net:/foo/bar*). Currently supported
- values for **NS** are: *ipc*, *net*, *time*, *user*, and *uts*.
- This option doesn't support joining a PID namespace, however, this is
- possible using *--external* and *--inheritfd*. 'EXTRA_OPTS' is optional
- and can be used to specify UID and GID for user namespace (e.g.,
- *--join-ns user:PID,UID,GID*).
-
-*--manage-cgroups* ['mode']::
- Restore cgroups configuration associated with a task from the image.
- Controllers are always restored in an optimistic way -- if already present
- in system, *criu* reuses it, otherwise it will be created.
-+
-The 'mode' may be one of the following:
-
- *none*::: Do not restore cgroup properties but require cgroup to
- pre-exist at the moment of *restore* procedure.
-
- *props*::: Restore cgroup properties and require cgroup to pre-exist.
-
- *soft*::: Restore cgroup properties if only cgroup has been created
- by *criu*, otherwise do not restore properties. This is the
- default if mode is unspecified.
-
- *full*::: Always restore all cgroups and their properties.
-
- *strict*::: Restore all cgroups and their properties from the scratch,
- requiring them to not present in the system.
-
- *ignore*::: Don't deal with cgroups and pretend that they don't exist.
-
-*--cgroup-yard* 'path'::
- Instead of trying to mount cgroups in CRIU, provide a path to a directory
- with already created cgroup yard. For more information look in the *dump*
- section.
-
-*--cgroup-root* ['controller'*:*]/'newroot'::
- Change the root cgroup the controller will be installed into. No controller
- means that root is the default for all controllers not specified.
+*--close* 'fd'::
+ Close file with descriptor 'fd' before anything else.
*--tcp-established*::
- Restore previously dumped established TCP connections. This implies that
- the network has been locked between *dump* and *restore* phases so other
- side of a connection simply notice a kind of lag.
-
-*--tcp-close*::
- Restore connected TCP sockets in closed state.
-
-*--veth-pair* __IN__**=**__OUT__::
- Correspondence between outside and inside names of veth devices.
-
-*-l*, *--file-locks*::
- Restore file locks from the image.
-
-*--lsm-profile* __type__**:**__name__::
- Specify an LSM profile to be used during restore. The _type_ can be
- either *apparmor* or *selinux*.
-
-*--lsm-mount-context* 'context'::
- Specify a new mount context to be used during restore.
-+
-This option will only replace existing mount context information
-with the one specified with this option. Mounts without the
-'context=' option will not be changed.
-+
-If a mountpoint has been checkpointed with an option like
-
- context="system_u:object_r:container_file_t:s0:c82,c137"
-+
-it is possible to change this option using
-
- --lsm-mount-context "system_u:object_r:container_file_t:s0:c204,c495"
-+
-which will result that the mountpoint will be restored
-with the new 'context='.
-+
-This option is useful if using *selinux* and if the *selinux*
-labels need to be changed on restore like if a container is
-restored into an existing Pod.
-
-*--auto-dedup*::
- As soon as a page is restored it get punched out from image.
+ Checkpoint/restore established TCP connections.
*-j*, *--shell-job*::
- Restore shell jobs, in other words inherit session and process group
- ID from the criu itself.
+ Allow to dump and restore shell jobs. This implies the restored task
+ will inherit session and process group ID from the criu itself.
+ Also this option allows one to migrate a single external tty connection, in other
+ words this option allows one to migrate such application as *top* and friends.
-*--cpu-cap* ['cap'[,'cap'...]]::
- Specify CPU capabilities to be present on the CPU the process is
- restoring. To inverse a capability, prefix it with *^*. This option implies
- that *--cpu-cap* has been passed on *dump* as well, except *fpu* option
- case. The 'cap' argument can be the following (or a set of comma-separated
- values):
+*-l*, *--file-locks*::
+ Allow to dump and restore file locks. It is necessary to make sure that
+ all file lock users are taken into dump, so it is only safe to use this
+ for a container dump/restore.
- *all*::: Require all capabilities. This is *default* mode if *--cpu-cap*
- is passed without arguments. Most safe mode.
+*--ms*::
+ In case of *check* command does not try to check for features which are
+ known to be not yet merged upstream.
- *cpu*::: Require the CPU to have all capabilities in image to match
- runtime CPU.
+*--page-server*::
+ In case of *dump* command sends pages to a page server.
- *fpu*::: Require the CPU to have compatible FPU. For example the process
- might be dumped with xsave capability but attempted to restore
- without it present on target CPU. In such case we refuse to
- proceed. This is *default* mode if *--cpu-cap* is not present
- in command line. Note this argument might be passed even if
- on the *dump* no *--cpu-cap* have been specified because FPU
- frames are always encoded into images.
+*--address*::
+ Page server address.
- *ins*::: Require CPU compatibility on instructions level.
-
- *none*::: Ignore capabilities. Most dangerous mode. The behaviour is
- implementation dependent. Try to not use it until really
- required.
-+
-For example, this option can be used in case *--cpu-cap=cpu* was used
-during *dump*, and images are migrated to a less capable CPU and are
-to be restored. By default, *criu* shows an error that CPU capabilities
-are not adequate, but this can be suppressed by using *--cpu-cap=none*.
-
-*--weak-sysctls*::
- Silently skip restoring sysctls that are not available. This allows
- to restore on an older kernel, or a kernel configured without some
- options.
-
-*--lazy-pages*::
- Restore the processes without filling out the entire memory
- contents. When this option is used, *restore* sets up the
- infrastructure required to fill memory pages either on demand when
- the process accesses them or in the background without stopping the
- restored process.
- This option requires running *lazy-pages* daemon.
-
-*--file-validation* ['mode']::
- Set the method to be used to validate open files. Validation is done
- to ensure that the version of the file being restored is the same
- version when it was dumped.
-+
-The 'mode' may be one of the following:
-
- *filesize*:::
- To explicitly use only the file size check all the time.
- This is the fastest and least intensive check.
-
- *buildid*:::
- To validate ELF files with their build-ID. If the
- build-ID cannot be obtained, 'chksm-first' method will be
- used. This is the default if mode is unspecified.
-
-*--image-io-mode* ['mode']::
- Set the I/O mode used when writing and reading the memory pages
- image. It controls whether the host page cache is used. The mode is
- selected independently for *dump* and *restore*; the image bytes are
- identical either way.
-+
-The 'mode' may be one of the following:
-
- *writeback*:::
- Buffered I/O via the host page cache. This is the default.
-
- *direct*:::
- Use O_DIRECT to bypass the host page cache. If the
- filesystem does not support O_DIRECT, *criu* falls back to
- buffered I/O.
-
-*--skip-file-rwx-check*::
- Skip checking file permissions (r/w/x for u/g/o) on restore.
-
-*--allow-uprobes*::
- Required when dumped with this option. Refer to this option in the section
- on dumping for more details.
-
-*check*
-~~~~~~~
-Checks whether the kernel supports the features needed by *criu* to
-dump and restore a process tree.
-
-There are three categories of kernel support, as described below. *criu
-check* always checks Category 1 features unless *--feature* is specified
-which only checks a specified feature.
-
-*Category 1*::: Absolutely required. These are features like support for
- */proc/PID/map_files*, *NETLINK_SOCK_DIAG* socket
- monitoring, */proc/sys/kernel/ns_last_pid* etc.
-
-*Category 2*::: Required only for specific cases. These are features
- like AIO remap, */dev/net/tun* and others that are only
- required if a process being dumped or restored
- is using those.
-
-*Category 3*::: Experimental. These are features like *task-diag* that
- are used for experimental purposes (mostly
- during development).
-
-If there are no errors or warnings, *criu* prints "Looks good." and its
-exit code is 0.
-
-A missing Category 1 feature causes *criu* to print "Does not look good."
-and its exit code is non-zero.
-
-Missing Category 2 and 3 features cause *criu* to print "Looks good but
-..." and its exit code is be non-zero.
-
-Without any options, *criu check* checks Category 1 features. This
-behavior can be changed by using the following options:
-
-*--extra*::
- Check kernel support for Category 2 features.
-
-*--experimental*::
- Check kernel support for Category 3 features.
-
-*--all*::
- Check kernel support for Category 1, 2, and 3 features.
-
-*--feature* 'name'::
- Check a specific feature. If 'name' is *list*, a list of valid
- kernel feature names that can be checked will be printed.
-
-*page-server*
-~~~~~~~~~~~~~
-Launches *criu* in page server mode.
-
-*--daemon*::
- Runs page server as a daemon (background process).
-
-*--status-fd*::
- Write \0 to the FD and close it once page-server is ready to handle
- requests. The status-fd allows to not daemonize a process and get its
- exit code at the end.
- It isn't supposed to use --daemon and --status-fd together.
-
-*--address* 'address'::
- Page server IP address or hostname.
-
-*--port* 'number'::
+*--port*::
Page server port number.
-*--ps-socket* 'fd'::
- Use provided file descriptor as socket for incoming connection.
- In this case --address and --port are ignored.
- Useful for intercepting page-server traffic e.g. to add encryption
- or authentication.
+*-V, *--version*::
+ Print program version.
-*--lazy-pages*::
- Serve local memory dump to a remote *lazy-pages* daemon. In this
- mode the *page-server* reads local memory dump and allows the
- remote *lazy-pages* daemon to request memory pages in random
- order.
+*-h*, *--help*::
+ Print inline help.
-*--tls-cacert* 'file'::
- Specifies the path to a trusted Certificate Authority (CA) certificate
- file to be used for verification of a client or server certificate.
- The 'file' must be in PEM format. When this option is used only the
- specified CA is used for verification. Otherwise, the system's trusted CAs
- and, if present, '/etc/pki/CA/cacert.pem' will be used.
+SYSCALLS EXECUTION
+------------------
-*--tls-cacrl* 'file'::
- Specifies a path to a Certificate Revocation List (CRL) 'file' which
- contains a list of revoked certificates that should no longer be trusted.
- The 'file' must be in PEM format. When this option is not specified, the
- file, if present, '/etc/pki/CA/cacrl.pem' will be used.
+To run a system call from another task\'s context use
-*--tls-cert* 'file'::
- Specifies a path to a file that contains a X.509 certificate to present
- to the remote entity. The 'file' must be in PEM format. When this option
- is not specified, the default location ('/etc/pki/criu/cert.pem') will be
- used.
+ criu exec -t pid syscall-string
-*--tls-key* 'file'::
- Specifies a path to a file that contains TLS private key. The 'file' must
- be in PEM format. When this option is not the default location
- ('/etc/pki/criu/private/key.pem') will be used.
+command. The 'syscall-string' should look like
-*--tls*::
- Use TLS to secure remote connections.
+ syscall-name syscall-arguments ...
-*lazy-pages*
-~~~~~~~~~~~~
-Launches *criu* in lazy-pages daemon mode.
+Each command line argument is transformed into the system call argument by
+the following rules:
-The *lazy-pages* daemon is responsible for managing user-level demand
-paging for the restored processes. It gets information required to
-fill the process memory pages from the *restore* and from the
-checkpoint directory. When a restored process access certain memory
-page for the first time, the *lazy-pages* daemon injects its contents
-into the process address space. The memory pages that are not yet
-requested by the restored processes are injected in the background.
+* If one starts with *&*, the rest of it gets copied to the target task\'s
+ address space and the respective syscall argument is the pointer to this
+ string;
-*exec*
-~~~~~~
-Executes a system call inside a destination task\'s context. This functionality
-is deprecated; please use *Compel* instead.
-
-*service*
-~~~~~~~~~
-Launches *criu* in RPC daemon mode, where *criu* is listening for
-RPC commands over socket to perform. This is convenient for a
-case where daemon itself is running in a privileged (superuser) mode
-but clients are not.
-
-dedup
-~~~~~
-Starts pagemap data deduplication procedure, where *criu* scans over all
-pagemap files and tries to minimize the number of pagemap entries by
-obtaining the references from a parent pagemap image.
-
-cpuinfo dump
-~~~~~~~~~~~~
-Fetches current CPU features and write them into an image file.
-
-cpuinfo check
-~~~~~~~~~~~~~
-Fetches current CPU features (i.e. CPU the *criu* is running on) and test if
-they are compatible with the ones present in an image file.
-
-
-CONFIGURATION FILES
--------------------
-*Criu* supports usage of configuration files to avoid the need of writing every
-option on command line, which is useful especially with repeated usage of
-same options. A specific configuration file can be passed with
-the "*--config* 'file'" option. If no file is passed, the default configuration
-files '/etc/criu/default.conf' and '$HOME/.criu/default.conf' are parsed (if
-present on the system). If the environment variable CRIU_CONFIG_FILE is set,
-it will also be parsed.
-
-The options passed to CRIU via CLI, RPC or configuration file are evaluated
-in the following order:
-
- - apply_config(/etc/criu/default.conf)
- - apply_config($HOME/.criu/default.conf)
- - apply_config(CRIU_CONFIG_FILE)
- - apply_config(*--config* 'file')
- - apply_config(CLI) or apply_config(RPC)
- - apply_config(RPC configuration file) (only for RPC mode)
-
-Default configuration file parsing can be deactivated
-with "*--no-default-config*" if needed. Parsed configuration files are merged
-with command line options, which allows overriding boolean options.
-
-Configuration file syntax
-~~~~~~~~~~~~~~~~~~~~~~~~~
-Comments are supported using \'#' sign. The rest of the line is ignored.
-Options are the same as command line options without the \'--' prefix, use
-one option per line (with corresponding argument if applicable, divided by
-whitespaces). If needed, the argument can be provided in double quotes (this
-should be needed only if the argument contains whitespaces). In case this type
-of argument contains a literal double quote as well, it can be escaped using
-the \'\' sign. Usage of commands is disallowed and all other escape sequences
-are interpreted literally.
-
-Example of configuration file to illustrate syntax:
----------------
-$ cat ~/.criu/default.conf
-tcp-established
-work-dir "/home/USERNAME/criu/my \"work\" directory"
-#this is a comment
-no-restore-sibling # this is another comment
----------------
-
-Configuration files in RPC mode
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Not only does *criu* evaluate configuration files in CLI mode, it also
-evaluates configuration files in RPC mode. Just as in CLI mode the
-configuration file values are evaluated first. This means that any option
-set via RPC will overwrite the configuration file setting. The user can
-thus change *criu*'s default behavior but it is not possible to change
-settings which are explicitly set by the RPC client.
-
-The RPC client can, however, specify an additional configuration file
-which will be evaluated after the RPC options (see above for option evaluation
-order). The RPC client can specify this additional configuration file
-via "req.opts.config_file = '/path/to/file'". The values from this
-configuration file will overwrite all other configuration file settings
-or RPC options. *This can lead to undesired behavior of criu and
-should only be used carefully.*
-
-NON-ROOT
---------
-*criu* can be used as non-root with either the *CAP_SYS_ADMIN* capability
-or with the *CAP_CHECKPOINT_RESTORE* capability introduces in Linux kernel 5.9.
-*CAP_CHECKPOINT_RESTORE* is the minimum that is required.
-
-*criu* also needs either *CAP_SYS_PTRACE* or a value of 0 in
-*/proc/sys/kernel/yama/ptrace_scope* (see *ptrace*(2)) to be able to interrupt
-the process for dumping.
-
-Running *criu* as non-root has many limitations and depending on the process
-to checkpoint and restore it may not be possible.
-
-In addition to *CAP_CHECKPOINT_RESTORE* it is possible to give *criu* additional
-capabilities to enable additional features in non-root mode.
-
-Currently *criu* can benefit from the following additional capabilities:
-
- - *CAP_NET_ADMIN*
- - *CAP_SYS_CHROOT*
- - *CAP_SETUID*
- - *CAP_SYS_RESOURCE*
-
-Note that for some operations, having a capability in a namespace other than
-the init namespace (i.e. the default/root namespace) is not sufficient. For
-example, in order to read symlinks in proc/[pid]/map_files CRIU requires
-CAP_CHECKPOINT_RESTORE in the init namespace; having CAP_CHECKPOINT_RESTORE
-while running in another user namespace (e.g. in a container) does not allow
-CRIU to read symlinks in /proc/[pid]/map_files.
-
-Without access to /proc/[pid]/map_files checkpointing/restoring processes
-that have mapped deleted files may not be possible.
-
-Independent of the capabilities it is always necessary to use "*--unprivileged*" to
-accept *criu*'s limitation in non-root mode.
+* Otherwise it is treated as a number (converted with strtol) and is directly
+ passed into the system call.
EXAMPLES
--------
+
To checkpoint a program with pid of *1234* and write all image files into
directory *checkpoint*:
-----------
criu dump -D checkpoint -t 1234
-----------
To restore this program detaching criu itself:
-----------
- criu restore -d -D checkpoint
-----------
+ criu restore -d -D checkpoint -t 1234
+
+
+To close a file descriptor number *1* in task with pid *1234*:
+
+ criu exec -t 1234 close 1
+
+To open a file named */foo/bar* for read-write in the task with pid
+*1234*:
+
+ criu exec -t 1234 open '&/foo/bar' 2
AUTHOR
------
-The CRIU team.
-
+OpenVZ team.
COPYRIGHT
---------
-Copyright \(C) 2011-2016, Parallels Holdings, Inc.
+Copyright \(C) 2011-2013, Parallels Inc.
diff --git a/Documentation/custom.xsl b/Documentation/custom.xsl
deleted file mode 100644
index 663717ed3..000000000
--- a/Documentation/custom.xsl
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
- 1
- 1
- 1
-
-
diff --git a/Documentation/logo.svg b/Documentation/logo.svg
deleted file mode 100644
index f713e72b7..000000000
--- a/Documentation/logo.svg
+++ /dev/null
@@ -1,136 +0,0 @@
-
-
-
-
diff --git a/Documentation/under-the-hood/32bit-tasks-cr.md b/Documentation/under-the-hood/32bit-tasks-cr.md
deleted file mode 100644
index 6a76a08db..000000000
--- a/Documentation/under-the-hood/32bit-tasks-cr.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# 32-bit tasks C/R
-
-## Compatible applications
-
-On x86_64, there are two types of compatibility mode applications:
-- ia32: Compiled to run on an i686 target, these can be executed on x86_64 if the `IA32_EMULATION` configuration option is enabled.
-- x32: Specially compiled binaries designed to run on x86_64 with the `CONFIG_X86_X32` configuration option enabled.
-
-Both use 4-byte pointers and thus can address no more than 4 GB of virtual memory.
-However, x32 uses the full 64-bit register set and therefore cannot be launched natively on an i686 host.
-Both require an additional environment on x86_64, such as Glibc, libraries, and compiler support.
-x32 is rarely distributed; currently, only the [Debian x32 port](https://wiki.debian.org/X32Port) is easily found.
-Currently, CRIU supports ia32 C/R. Support for x32 can be added relatively easily, as the necessary kernel patches for ia32 C/R are already in place.
-In this document, the terms *compatible* and *32-bit* refer to ia32 applications unless otherwise specified.
-
-## Difference between native and compatibility mode applications
-
-From the CPU's point of view, 32-bit compatibility mode applications differ from 64-bit applications by the current Code Segment (CS) selector. If the L-bit (Long mode) in the segment descriptor is set, the CPU operates in 64-bit mode when that descriptor is used. There are other differences between 32-bit and 64-bit selectors; for more details, see [the article "The 0x33 Segment Selector (Heavens Gate)"](https://www.malwaretech.com/2014/02/the-0x33-segment-selector-heavens-gate.html). Code selectors for both modes are defined in kernel headers as `__USER32_CS` and `__USER_CS`, corresponding to descriptors in the Global Descriptor Table (GDT). The mode can be switched from 64-bit to compatibility mode by changing the CS value (e.g., using a long jump).
-
-From the Linux kernel's point of view, applications differ based on values set during `exec`, such as `mmap_base` or thread info flags like `TIF_ADDR32`, `TIF_IA32`, or `TIF_X32`.
-Both native and compatibility mode applications can perform either 32-bit or 64-bit syscalls.
-
-## Mixed-bitness applications
-
-The current kernel ABI allows for the creation of mixed-bitness applications, which can become quite complex.
-For instance, an application could set both 32-bit and 64-bit robust futex list pointers.
-Alternatively, a multi-threaded application could have some threads executing 32-bit code while others execute 64-bit code.
-
-If support for such mixed-bitness applications is ever needed, it could be added to CRIU relatively easily. However, this should likely be a compile-time configuration option to avoid adding unnecessary syscalls to standard C/R operations.
-
-Currently, there are no plans to add this support, as such applications are unlikely to be encountered outside of synthetic tests.
-
-## Approaches to C/R for compatibility mode applications
-
-32-bit C/R can be implemented in several ways. This section describes the pros and cons of various approaches and explains why the current implementation was chosen.
-
-### Restore via exec() of a 32-bit dummy binary vs. from 64-bit CRIU
-
-Restoring a 32-bit application could be done using a 32-bit daemon that communicates with the 64-bit CRIU binary or a 32-bit CRIU subprocess.
-
-**Pros**:
-- No kernel patches expected (though `vDSO mremap()` would still require support).
-
-**Cons**:
-- The CRIU codebase lacks a dedicated restore daemon, requiring significant rework.
-- A 64-bit application can have a 32-bit child, which in turn could parent a 64-bit process. This would require re-executing the native 64-bit CRIU from the 32-bit dummy or subprocess.
-- It would be necessary to send process properties, open image file descriptors, and shared memory containing the parsed `ps_tree` to the daemon. The volume of IPC calls would slow down the restoration process.
-- Restoration becomes more complex, especially when considering user and PID namespaces.
-- Task properties that are erased during `exec()` cannot benefit from optimized inheritance.
-- A separate daemon would also be needed for x32.
-
-### Restore with a flag to sigreturn() or arch_prctl()
-
-The initial attempt to implement 32-bit C/R was rejected by the LKML community for several reasons. It involved swapping thread info flags (e.g., `TIF_ADDR32`, `TIF_IA32`, `TIF_X32`), unmapping the native 64-bit vDSO, and mapping the 32-bit vDSO based on a bit in the `rt_sigreturn()` sigframe or a dedicated `arch_prctl()` call.
-
-**Pros**:
-- Simple for CRIU: just perform a `sigreturn` with the new bit set or call `arch_prctl` before `sigreturn`.
-
-**Cons**:
-- If the 32-bit vDSO on the restoration host differs from the dumped image, the task must be intercepted after `sigreturn` to create jump trampolines (this is simpler with `arch_prctl`).
-- Too many potential failure points for a single syscall; overly complex.
-- Allowing userspace to swap thread info flags could introduce new race conditions and bugs (e.g., since the `TASK_SIZE` macro depends on `TIF_ADDR32`, memory mapping behavior might become unpredictable).
-
-Following LKML discussions, it was decided to separate personality changes from the vDSO mapping API, remove the `TIF_IA32` flag that distinguished 32-bit from 64-bit tasks, and instead rely on the nature of the syscall (compat, x32, or native).
-
-### Seizing with separate 32-bit and 64-bit parasites
-
-**Pros**:
-- No 32-bit calls in the 64-bit parasite and vice-versa.
-- Since `ptrace` does not allow setting a 32-bit register set on a 64-bit task (and vice versa), using a parasite of the same nature as the task avoids these limitations.
-
-**Cons**:
-- Requires maintaining two or three (for x32) separate parasite blobs.
-- Requires complex Makefile macros to build multiple parasites.
-- Serializing parasite responses is difficult because argument sizes differ between modes, leading to complex and less readable C macros.
-
-### Current approach
-
-CRIU (a 64-bit process) handles 32-bit (ia32) tasks through a series of architecture-specific transitions:
-
-1. **Architecture Detection**: CRIU uses `ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov)` to detect the task's architecture. The kernel returns different register set sizes depending on the mode: `sizeof(user_regs_struct64)` for native 64-bit tasks and `sizeof(user_regs_struct32)` for 32-bit compatibility mode tasks.
-2. **Dumping**: When dumping a 32-bit task, CRIU uses the 64-bit `ptrace` interface. The kernel handles the internal mapping of 32-bit registers into the structure expected by CRIU.
-3. **vDSO Handling**: To ensure the restored task uses a vDSO compatible with the current kernel, CRIU uses the `arch_prctl(ARCH_MAP_VDSO_32, addr)` system call (available since kernel v4.8) to map the 32-bit vDSO into the restored process's address space.
-4. **Restoration via Sigreturn**: The final restoration of 32-bit registers is performed using a 32-bit `rt_sigreturn` call:
- * CRIU prepares a 32-bit signal frame (`rt_sigframe_ia32`) on the target task's stack.
- * The CRIU restorer code, running in 64-bit mode, executes a far return (`lretq`) to switch the CPU to 32-bit mode with the `USER32_CS` (0x23) segment selector.
- * Once in 32-bit mode, it executes `int $0x80` with the `__NR32_rt_sigreturn` syscall number. The kernel then restores all registers from the 32-bit sigframe and resumes the task in 32-bit mode.
-
-## To-Do
-
-### vsyscall page handling
-
-The `vsyscall` page is an emulated, fixed-address page (`0xffffffffff600000`) used for legacy support. It is not a standard VMA and is marked as `VMA_AREA_VSYSCALL` by CRIU, which avoids dumping or restoring its contents. Since its presence in `/proc//maps` depends on kernel configuration (`vsyscall=emulate` or `vsyscall=xonly`), it can introduce noise during ZDTM tests that compare memory layouts. Consequently, tests are often run with `vsyscall=none`.
-
-### Error reporting on x32 binary dumping
-
-Currently, CRIU does not support x32 binaries (64-bit registers with 32-bit pointers). While the infrastructure for 32-bit pointers exists, the specific register handling and vDSO mapping for x32 are not implemented. Attempting to dump an x32 binary should result in an explicit error.
-
-### Removal of TIF_IA32 from the kernel
-
-The `TIF_IA32` thread info flag was historically used to distinguish 32-bit tasks. Kernel efforts (merged in v5.11) have moved towards relying on the nature of the syscall (compat vs. native) rather than a persistent thread flag. This unification simplifies how the kernel and CRIU interact, particularly for tracing tools like uprobes.
-
-## External links
-- [GitHub issue](https://github.com/checkpoint-restore/criu/issues/43)
-
diff --git a/Documentation/under-the-hood/README.md b/Documentation/under-the-hood/README.md
deleted file mode 120000
index dd0ea36c8..000000000
--- a/Documentation/under-the-hood/README.md
+++ /dev/null
@@ -1 +0,0 @@
-index.md
\ No newline at end of file
diff --git a/Documentation/under-the-hood/aio.md b/Documentation/under-the-hood/aio.md
deleted file mode 100644
index cd50ecd97..000000000
--- a/Documentation/under-the-hood/aio.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# Asynchronous I/O (AIO)
-
-CRIU supports checkpointing and restoring kernel-level Asynchronous I/O (AIO) contexts, which are managed via the `io_setup`, `io_submit`, `io_getevents`, and `io_destroy` system calls.
-
-## How CRIU Handles AIO
-
-To successfully checkpoint and restore an AIO context, CRIU manages three primary components:
-
-1. **The AIO Ring Buffer**: This is a memory-mapped area where the kernel and userspace communicate. CRIU identifies these areas by their `[aio]` label in `/proc/pid/maps` or by detecting the specific VMA attributes.
-2. **Completed Events**: Events that have finished and are already residing in the ring buffer are dumped as part of the process's memory.
-3. **AIO Context State**: This includes the kernel's internal tracking of the ring's head and tail.
-
-### The Restoration Process
-
-The restoration of an AIO ring is complex because the kernel's AIO context ID (the `aio_context_t` value) is an internal pointer that cannot be arbitrarily assigned by userspace. CRIU uses the following strategy to restore it:
-
-1. **New Ring Creation**: The restorer calls `io_setup` to create a fresh AIO ring with the original number of requested events.
-2. **Tail Synchronization**: To move the kernel's internal `tail` pointer to the original position, CRIU submits dummy I/O requests (typically writes to `/dev/null`). Since these operations are synchronous for the device, the kernel advances the tail as each request completes.
-3. **Head Synchronization**: CRIU manually adjusts the `head` pointer in the ring header to match the state at the time of the dump.
-4. **Event Data Restoration**: The original `io_events` data (the completed but unread events) is copied from the dump image into the new ring buffer.
-5. **Memory Remapping**: Finally, CRIU uses `mremap` to move the new ring buffer to its original virtual address, ensuring the application can continue using its existing AIO context ID.
-
-## Limitations: In-Flight Events
-
-Currently, **in-flight events** (I/O requests that have been submitted but not yet completed at the time of the dump) are **not supported**.
-
-* **Dumping**: CRIU's parasite code checks for AIO rings but does not currently wait for pending requests to complete. If a request completes during or after the dump, it may lead to data inconsistency or a failed restore.
-* **Restoring**: There is no mechanism to re-submit pending I/O requests upon restoration. Applications using AIO should ideally be in a quiescent state (all submitted I/O completed) before being checkpointed.
-
-## See also
-
-* [Memory dumping and restoring](memory-dumping-and-restoring.md)
diff --git a/Documentation/under-the-hood/apparmor.md b/Documentation/under-the-hood/apparmor.md
deleted file mode 100644
index ef8bb1eff..000000000
--- a/Documentation/under-the-hood/apparmor.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# AppArmor Support
-
-CRIU provides support for checkpointing and restoring **AppArmor** security profiles and namespaces. This is a critical feature for containerized environments (like Docker, LXC, or Podman) where each container frequently operates under its own set of specialized security policies.
-
-## How CRIU Handles AppArmor
-
-AppArmor integration in CRIU ensures that restored processes continue to operate under the same security constraints as the original processes, while also managing the temporary permissions needed for the checkpointing process itself.
-
-### 1. Checkpointing (Dumping)
-During the dump phase, CRIU detects the AppArmor state of each task:
-* **Profile Identification**: CRIU captures the active profile name for every thread (e.g., `unconfined`, `docker-default`, or a custom user-defined profile).
-* **Namespace and Policy Dumping**: In modern containerized setups, containers often have their own AppArmor namespaces. CRIU walks the `/sys/kernel/security/apparmor/policy/` directory to capture the full hierarchy of namespaces and the raw binary blobs of all loaded policies.
-* **Parasite Profile**: To allow the [Parasite Code](parasite-code.md) to perform its necessary inspections (like opening network sockets or reading memory) without being blocked by the application's strict security policy, CRIU temporarily transitions the task into a special, permissive "parasite profile" while it is infected.
-
-### 2. Restoration
-Restoring AppArmor state involves re-establishing the security context before the process resumes:
-* **Policy Loading**: CRIU uses the `apparmor_parser` utility on the destination host to re-load the policy blobs captured in the image files.
-* **Namespace Reconstruction**: It recreates any nested AppArmor namespaces to match the original environment.
-* **Profile Re-attachment**: As each process is restored, CRIU ensures it is transitioned back into its original profile (or stack of profiles) using the `aa_change_profile()` interface before the application code begins executing.
-
-## Support for Stacking
-
-Modern AppArmor implementations support **Profile Stacking**, where multiple security profiles are applied to a single process simultaneously (e.g., a container-wide profile plus a per-application profile). CRIU correctly identifies, dumps, and restores these complex stacked configurations.
-
-## Kernel Requirements
-
-Reliable AppArmor C/R requires:
-* A kernel with `CONFIG_SECURITY_APPARMOR` enabled and active.
-* The `securityfs` filesystem mounted (typically at `/sys/kernel/security`).
-* Support for AppArmor policy introspection and namespaces, which is standard in modern distributions like Ubuntu and Debian.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Parasite Code](parasite-code.md)
-* [Kerndat Feature Detection](kerndat.md)
diff --git a/Documentation/under-the-hood/arm64-gcs.md b/Documentation/under-the-hood/arm64-gcs.md
deleted file mode 100644
index e27a93083..000000000
--- a/Documentation/under-the-hood/arm64-gcs.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# ARM64 Guarded Control Stack (GCS)
-
-CRIU supports checkpointing and restoring the **Guarded Control Stack (GCS)** feature on ARM64 (AArch64) architectures. GCS is a hardware-assisted shadow stack mechanism designed to prevent return-oriented programming (ROP) attacks by maintaining a protected stack of return addresses.
-
-## How CRIU Handles GCS
-
-GCS support is integrated into CRIU's architecture-specific code for AArch64 (`arch/aarch64/gcs.c`).
-
-### 1. Checkpointing (Dumping)
-During the dump phase, CRIU detects if a task has GCS enabled by checking its CPU features and hardware capabilities (`HWCAP_GCS`).
-* **State Capture**: CRIU uses `ptrace(PTRACE_GETREGSET, ..., NT_ARM_GCS, ...)` to retrieve the current GCS state.
-* **Key Parameters**:
- * `gcspr_el0`: The current Guarded Control Stack Pointer.
- * `features_enabled`: The GCS configuration flags (e.g., `PR_SHADOW_STACK_ENABLE`).
-* **VMA Identification**: CRIU identifies the memory region (VMA) used for the shadow stack, which is marked with special kernel attributes.
-
-### 2. Restoration
-Restoring GCS requires carefully re-establishing the shadow stack before the process resumes normal execution.
-* **Shadow Stack Mapping**: CRIU uses the `map_shadow_stack` system call to recreate the shadow stack at its original virtual address.
-* **Context Setup**: The captured GCS state (`gcspr_el0` and flags) is integrated into the task's **restorer context**.
-* **Sigframe Integration**: To ensure a seamless transition, CRIU places a `gcs_context` entry into the signal frame used for the final `sigreturn`. This informs the kernel to switch to the restored shadow stack as the process resumes.
-
-## Kernel Requirements
-
-GCS support in CRIU requires an ARM64 host and a kernel that supports the Guarded Control Stack ABI, typically including:
-* `PR_SHADOW_STACK_ENABLE` prctl support.
-* The `map_shadow_stack` system call.
-* `NT_ARM_GCS` ptrace regset.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Restorer Context](restorer-context.md)
diff --git a/Documentation/under-the-hood/bpf-maps.md b/Documentation/under-the-hood/bpf-maps.md
deleted file mode 100644
index 59de4e74a..000000000
--- a/Documentation/under-the-hood/bpf-maps.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# BPF Maps
-
-BPF maps are kernel objects that store data used by BPF programs, typically in the form of key-value pairs. Applications access these maps via file descriptors. Checkpointing and restoring BPF maps involves serializing both their **metadata** and their **data contents**.
-
-## How CRIU Handles BPF Maps
-
-### Metadata Serialization
-CRIU collects essential map attributes from several sources:
-- **/proc filesystem**: Essential fields such as `map_type`, `key_size`, `value_size`, `max_entries`, and the `frozen` status are parsed from the task's `fdinfo`.
-- **BPF System Call**: CRIU uses the `bpf` system call with the `BPF_OBJ_GET_INFO_BY_FD` command to retrieve additional information, including the map name and interface index (`ifindex`).
-
-### Data Serialization
-To preserve the map's contents, CRIU relies on batch operations:
-- **Dumping**: During the checkpoint stage, CRIU uses `BPF_MAP_LOOKUP_BATCH` to efficiently read all key-value pairs from the map.
-- **Restoring**: During the restore phase, CRIU recreates the map and uses `BPF_MAP_UPDATE_BATCH` to repopulate it with the saved key-value pairs.
-
-### Supported Map Types
-CRIU currently supports data serialization for the following BPF map types:
-- `BPF_MAP_TYPE_HASH`
-- `BPF_MAP_TYPE_ARRAY`
-
-For other map types, CRIU may be able to restore the map itself (metadata) but not its contents, depending on kernel support for batch operations on those types.
-
-### Frozen Maps
-If a BPF map was marked as read-only (frozen) using `bpf_map_freeze()`, CRIU detects this state from `fdinfo` and reapplies the freeze during restoration after the data has been repopulated.
-
-## To-Do
-
-- **BTF Support**: Serialization and restoration of BPF Type Format (BTF) information associated with maps.
-- **Extended Map Types**: Implementation of data serialization for more BPF map types (e.g., `BPF_MAP_TYPE_PERF_EVENT_ARRAY`, `BPF_MAP_TYPE_LPM_TRIE`).
-- **Map Extra Data**: Full support for `map_extra` fields introduced in recent kernels (currently only partially parsed with limited restoration).
-
-## External Links
-- [BPF Documentation](https://www.kernel.org/doc/html/latest/bpf/index.html)
-- [Notes on BPF](https://blogs.oracle.com/linux/notes-on-bpf-1)
-- [An eBPF Overview](https://www.collabora.com/news-and-blog/blog/2019/04/05/an-ebpf-overview-part-1-introduction/)
diff --git a/Documentation/under-the-hood/cgroups.md b/Documentation/under-the-hood/cgroups.md
deleted file mode 100644
index 577829a70..000000000
--- a/Documentation/under-the-hood/cgroups.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# CGroups
-
-CRIU provides comprehensive support for checkpointing and restoring Control Groups (CGroups) for both cgroup v1 and cgroup v2.
-
-## Overview
-
-When managing CGroups, CRIU handles three main aspects:
-1. **Process Placement**: The specific cgroup sets (a list of controller/path pairs) that each task in the process tree belongs to.
-2. **Hierarchy and Properties**: The existing cgroup directory tree, its permissions, and various control properties (e.g., CPU shares, memory limits).
-3. **Namespace Boundaries**: Support for CGroup namespaces (`CLONE_NEWCGROUP`), ensuring that the restored tasks have the same view of the cgroup hierarchy.
-
-## Default Behavior
-
-By default, CRIU manages cgroups in **soft mode** (`--manage-cgroups=soft`). In this mode:
-* CRIU automatically dumps process cgroup memberships.
-* Upon restoration, it attempts to recreate the cgroup hierarchy and restore properties for cgroups that it created.
-* If a cgroup already exists, CRIU avoids overwriting its properties to prevent interference with other tasks on the system.
-
-## CGroup V2 Support
-
-CRIU fully supports the unified cgroup v2 hierarchy. Key features include:
-* **Global Properties**: Restoration of global v2 attributes such as `cgroup.subtree_control`, `cgroup.max.descendants`, and `cgroup.max.depth`.
-* **Process Migration**: Moving tasks between v2 cgroups using `cgroup.procs` (or `cgroup.threads` for threaded controllers).
-* **Freezer**: Integrated support for the cgroup v2 freezer mechanism (`cgroup.freeze`).
-
-## CGroup Namespaces
-
-CRIU leverages cgroup namespaces to accurately restore a container's view of the cgroup tree. During restoration:
-1. It identifies the cgroup namespace boundary (the path prefix) for each controller.
-2. It moves the root task into the appropriate cgroup relative to the host.
-3. It calls `unshare(CLONE_NEWCGROUP)` to pin the root of the cgroup namespace to that location, matching the original environment.
-
-## Mountpoints of the "cgroup" Filesystem
-
-CRIU supports dumping and restoring cgroup filesystem mountpoints. However, a significant limitation exists regarding bind-mounted subgroups:
-
-**Root Mount Requirement**: By default, CRIU expects to find the "root" mount of a cgroup controller (where the mount root is `/`) within the dumped mount namespace.
-* If a container has only bind-mounted **subgroups** (e.g., `/sys/fs/cgroup/memory/my-container` is bind-mounted to `/sys/fs/cgroup/memory`) without a corresponding root mount of that controller being visible, CRIU may fail the dump.
-* This is because CRIU needs to identify the full path of the cgroup relative to the hierarchy root to accurately reconstruct it.
-
-To overcome this, such mounts must often be treated as **external mounts** (`--external mnt[...]`) or the full hierarchy must be made visible to CRIU during the dump.
-
-## CGroups Restoration Strategy
-
-The `--manage-cgroups=MODE` option allows for fine-grained control:
-
-* `none`: Requires cgroups to pre-exist; does not restore properties.
-* `props`: Requires cgroups to pre-exist; restores properties from the image.
-* `soft` (Default): Restores properties only for cgroups created by CRIU.
-* `full`: Always recreates all cgroups and restores all properties.
-* `strict`: Recreates all cgroups from scratch; fails if any already exist.
-* `ignore`: Completely ignores cgroup information.
-
-## External CGroup Yard
-
-The `--cgroup-yard PATH` option allows CRIU to use a pre-mounted cgroup hierarchy located at `PATH`. This is particularly useful in unprivileged environments where CRIU may not have the `CAP_SYS_ADMIN` capability required to mount cgroup filesystems itself. For every cgroup mount, there should be exactly one directory named after the controller(s) co-mounted there (or "unified" for cgroup v2).
diff --git a/Documentation/under-the-hood/change-ip-address.md b/Documentation/under-the-hood/change-ip-address.md
deleted file mode 100644
index 5d1035848..000000000
--- a/Documentation/under-the-hood/change-ip-address.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Changing IP Addresses During Migration
-
-When performing a [live migration](live-migration.md) of a process between hosts, a common challenge is handling IP address changes. While the ideal solution often involves using containers with their own network namespaces and virtual IPs, migrating a service to a different physical IP address is sometimes necessary.
-
-## The Core Problem
-
-TCP connections are identified by a 4-tuple: (Source IP, Source Port, Destination IP, Destination Port). If either IP address changes during migration, the TCP stack on the peer will not recognize the migrated connection and will typically respond with a Reset (RST) or simply ignore the packets.
-
-Consequently, there are three scenarios to consider when changing IPs:
-
-### 1. Listening Sockets
-If a server is bound to `0.0.0.0` (INADDR_ANY), it will "just work" after migration, as it will listen on all available interfaces on the new host. However, if the server is bound to a specific IP address that does not exist on the destination host, restoration will fail unless the binding is updated.
-
-**Solutions:**
-- **CRIT**: Use the [CRIT](../crit.md) tool to manually edit the `inetsk.img` or `files.img` images to update the binding address.
-- **Plugins**: Use the `UPDATE_INETSK` plugin hook (see below) to programmatically change the IP address during restoration.
-
-### 2. In-Flight Connections
-These are connections that have been initiated but not yet accepted by the application. CRIU provides the `--skip-in-flight` option to ignore these connections during the dump.
-
-### 3. Established Sockets
-These are active connections. Changing the IP address of an established socket is technically possible but will usually break the connection unless specialized network-level translation (like NAT) is used.
-
-**CRIU Solutions:**
-- **--tcp-close**: This option tells CRIU to dump established connections but restore them in a closed state. This prevents application-level errors caused by "holes" in the file descriptor table while acknowledging that the specific network connection is terminated.
-- **--tcp-established**: Used in combination with IP translation mechanisms (like NAT or proxies), this allows the connection to be restored.
-
-## Programmatic IP Remapping (Plugins)
-
-CRIU provides a plugin hook, `UPDATE_INETSK`, specifically for modifying socket attributes during restoration. A plugin can implement this hook to intercept the restoration of an INET socket and change its source or destination IP addresses.
-
-```c
-/* Plugin hook signature in criu-plugin.h */
-int cr_plugin_update_inetsk(uint32_t family, uint32_t state, uint32_t *src_ip, uint32_t *dst_ip);
-```
-
-By modifying `src_ip` and `dst_ip` within the plugin, you can redirect sockets to new addresses as they are being recreated.
-
-## Summary of Options
-
-| Scenario | Recommendation | CRIU Flag / Tool |
-| :--- | :--- | :--- |
-| **Old IP not on new host** | Remap local binding | `CRIT` or `UPDATE_INETSK` plugin |
-| **In-Flight Connection** | Ignore | `--skip-in-flight` |
-| **Established Connection** | Terminate gracefully | `--tcp-close` |
-| **Established Connection** | Maintain (requires NAT) | `--tcp-established` + remapping |
diff --git a/Documentation/under-the-hood/checkpointrestore.md b/Documentation/under-the-hood/checkpointrestore.md
deleted file mode 100644
index 9b75d4c31..000000000
--- a/Documentation/under-the-hood/checkpointrestore.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Checkpoint and Restore Architecture
-
-This page describes the high-level design and internal mechanics of the Checkpoint and Restore processes in CRIU.
-
-## Checkpoint
-
-The checkpoint procedure captures the full state of a process tree. It combines information from the Linux kernel's `/proc` filesystem with data extracted directly from the processes' address space.
-
-### 1. Freezing the Process Tree
-CRIU begins by identifying the process group leader (via the `--tree` option) and recursively collecting all threads and children. To ensure a consistent snapshot, the entire tree must be "frozen."
-
-* **ptrace**: CRIU uses `PTRACE_SEIZE` followed by `PTRACE_INTERRUPT` to stop tasks without delivering signals that could be visible to the application.
-* **Freezer CGroup**: Alternatively, the [Freezer CGroup](freezing-the-tree.md) can be used to freeze all tasks in a single operation.
-
-### 2. Resource Collection (External State)
-CRIU gathers state that the kernel exposes via `/proc`:
-* **File Descriptors**: Parsed from `/proc/$pid/fdinfo` (which includes positions and flags).
-* **Memory Maps**: Captured from `/proc/$pid/smaps` and `/proc/$pid/map_files`.
-* **Core State**: Task statistics and basic identifiers from `/proc/$pid/stat`.
-
-### 3. Parasite Injection (Internal State)
-Some state (like memory contents and specific credentials) can only be captured from within the process. CRIU uses a technique called **parasite injection**:
-1. **Infection**: CRIU uses `ptrace` to inject a small bit of code into the task's instruction stream (at the current `CS:IP`).
-2. **Bootstrap**: This code executes an `mmap` syscall to allocate space for the full **parasite blob**.
-3. **Execution**: The parasite code runs as a daemon inside the task, communicating with the CRIU coordinator via a Unix socket to dump memory pages and other internal metadata.
-
-### 4. Cleanup
-Once the state is captured, CRIU uses `ptrace` to remove the parasite code and restore the original instructions. The processes are then either resumed or killed, depending on the command-line options.
-
----
-
-## Restore
-
-The restore procedure is essentially the reverse of a checkpoint. CRIU "morphs" itself into the process tree it is restoring through a multi-stage process.
-
-### 1. Resolve Shared Resources
-CRIU analyzes the image files to identify resources shared between processes (e.g., shared memory segments, pipes, or inherited file descriptors). It determines which process will "create" the resource and how others will "inherit" it.
-
-### 2. Fork the Process Tree
-CRIU calls `fork()` repeatedly to recreate the original process hierarchy. To restore specific PIDs, it uses the `ns_last_pid` interface or the `clone3` system call. At this stage, only process leaders are created; threads are restored later.
-
-### 3. Restore Basic Resources
-Each process in the new tree begins restoring its environment:
-* **Namespaces**: Joins or creates Network, Mount, UTS, and IPC namespaces.
-* **Files and Sockets**: Reopens file descriptors and recreates network sockets.
-* **Memory Prep**: Maps anonymous memory regions and fills them with data from the images.
-
-### 4. The Restorer Context
-To restore the final memory layout, CRIU must unmap its own code and data. This requires a **restorer blob**:
-* **Self-Contained**: The blob is a Position-Independent Executable (PIE) that contains all necessary logic to perform the final `mmap` and `munmap` calls.
-* **Non-Conflicting**: It is mapped into a "hole" in the task's address space that does not conflict with either CRIU's current mappings or the task's original mappings.
-* **Final Transition**: The process jumps into the restorer blob, which unmaps CRIU, maps the final memory regions, restores timers and credentials, and recreates any additional threads.
-
-### 5. Sigreturn
-The very last step of the restorer is to call `sigreturn`. CRIU prepares a special signal frame on the stack that contains the original register state (including the instruction pointer) of the process at the time of the checkpoint. The `sigreturn` syscall tells the kernel to load this state and resume execution of the application code.
-
-*See also: [Restorer Context](restorer-context.md), [Tree After Restore](tree-after-restore.md)*
diff --git a/Documentation/under-the-hood/code-blobs.md b/Documentation/under-the-hood/code-blobs.md
deleted file mode 100644
index cb83f888a..000000000
--- a/Documentation/under-the-hood/code-blobs.md
+++ /dev/null
@@ -1,68 +0,0 @@
-# Code Blobs and PIE Generation
-
-CRIU and its sub-project **Compel** use specialized binary blobs to execute code in environments where standard libraries and runtime environments are unavailable. These blobs are Position-Independent Executables (PIE) that are converted into C headers for easy integration into the main CRIU binary.
-
-## Why Code Blobs are Necessary
-
-CRIU operates in two primary scenarios that require these specialized environments:
-
-1. **Parasite Code Execution**: During a checkpoint, CRIU injects code into the target process's address space to extract internal state (like memory contents and credentials). This code must be self-contained and PIE-compiled to run at any address.
-2. **Restorer Context**: During restoration, the process must unmap its current memory (including CRIU's own code) and map the original memory of the checkpointed application. The code performing these operations must exist in a memory region that does not conflict with the target application's layout.
-
-## Building PIE Code Blobs
-
-The generation of these blobs is handled by the **Compel** utility. The process involves compiling C and assembly source files into a single ELF object and then using the `compel hgen` tool to transform that object into a C header.
-
-### The `compel hgen` Tool
-
-The `hgen` (header generator) tool performs the following tasks:
-1. **Relocation Extraction**: It identifies all symbols that require relocation and creates a structured `compel_reloc` array.
-2. **Binary Data Conversion**: It converts the allocated ELF sections (code and data) into a static C byte array.
-3. **Bootstrap Initialization**: It generates a setup function (e.g., `parasite_setup_c_header`) that populates a `parasite_blob_desc` structure, which CRIU uses to manage the blob's lifecycle.
-
-### Example Header Format
-
-The generated header file typically contains:
-
-```c
-/* Relocation information */
-static const struct compel_reloc parasite_relocs[] = {
- { .offset = 0x0000002c, .type = COMPEL_TYPE_INT, .addend = 0, .value = 0x12345678 },
- ...
-};
-
-/* The binary blob itself */
-static const char parasite_blob[] = {
- 0x48, 0x8d, 0x25, 0x2d, 0x60, 0x00, 0x00, 0x48,
- ...
-};
-
-/* Setup function for CRIU integration */
-static void parasite_setup_c_header_desc(struct parasite_blob_desc *pbd, bool native)
-{
- pbd->parasite_type = COMPEL_BLOB_CHEADER;
- pbd->hdr.mem = parasite_blob;
- pbd->hdr.bsize = sizeof(parasite_blob);
- ...
-}
-```
-
-## Build Procedure
-
-The build system follows these steps to generate the headers:
-
-1. **Compilation**: Source files (like `parasite.c` or `restorer.c`) are compiled with PIE flags (`-fpie`, `-ffreestanding`, `-nostdlib`).
-2. **Linking**: Object files are linked into a single `.built-in.o` file using a specialized linker script (`compel-pack.lds.S`) that organizes sections into a layout suitable for a standalone blob.
-3. **Header Generation**: The `compel hgen` command is executed on the linked object to produce the final `-blob.h` header.
-
-```bash
-# Example Makefile recipe
-$(obj)/parasite-blob.h: $(obj)/parasite.built-in.o
- compel hgen -f $< -o $@
-```
-
-## See also
-
-* [Parasite Code](parasite-code.md)
-* [Restorer Context](restorer-context.md)
-* [Compel Sub-project](../compel.md)
diff --git a/Documentation/under-the-hood/comparison-to-other-cr-projects.md b/Documentation/under-the-hood/comparison-to-other-cr-projects.md
deleted file mode 100644
index 353fc24a8..000000000
--- a/Documentation/under-the-hood/comparison-to-other-cr-projects.md
+++ /dev/null
@@ -1,63 +0,0 @@
-# Comparison to Other Checkpoint/Restore Projects
-
-This page explains the primary differences between CRIU and other checkpoint/restore (C/R) solutions available for Linux.
-
-## DMTCP (Distributed MultiThreaded Checkpointing)
-
-DMTCP implements checkpoint/restore at the library level. To use it, an application must be launched with the DMTCP library dynamically linked from the start. This library intercepts library calls, builds an internal shadow database of the process state, and forwards requests to `glibc` or the kernel.
-
-**Key Characteristics of DMTCP:**
-* **No Kernel Patches**: Works on standard kernels without requiring specific features.
-* **Library Level**: Intercepts calls at the userspace level, which can introduce performance overhead.
-* **PID Virtualization**: Since the kernel does not traditionally allow setting a specific PID during fork, DMTCP "fools" the application by intercepting `getpid()` and returning a fake value. This can be problematic if the application accesses `/proc` using its real PID.
-* **Limited API Coverage**: May not support all kernel APIs (e.g., `inotify` support is limited).
-
-In contrast, **CRIU** does not require pre-loading libraries. It uses standard kernel interfaces (extended where necessary for C/R) to transparently capture and restore arbitrary applications.
-
-## BLCR (Berkeley Lab Checkpoint/Restart)
-
-BLCR is a system-level checkpointer designed primarily for High Performance Computing (HPC) and MPI jobs. It is implemented as a loadable kernel module.
-
-**Key Characteristics of BLCR:**
-* **Kernel Module**: Requires a specific GPL-licensed kernel module.
-* **HPC Focused**: Optimized for CPU and memory-intensive batch jobs.
-* **Limited Scope**: Traditionally lacks support for complex modern features like namespaces, containers, or diverse socket types.
-
-## PinPlay
-
-PinPlay is a checkpointing tool built on top of Intel's PIN binary instrumentation tool. It is primarily used for deterministic replay and architectural simulation. It records architectural register state and memory pages, often focusing on reducing runtime for simulators.
-
-## OpenVZ (In-Kernel)
-
-Legacy OpenVZ (RHEL6 and earlier) featured an in-kernel C/R implementation. While highly efficient and robust for its time, it required a heavily patched kernel. CRIU was developed as the "user-space" successor to this technology, moving the logic out of the kernel to improve maintainability and facilitate upstream adoption.
-
----
-
-## Comparison Table
-
-| Feature | CRIU | DMTCP | BLCR | OpenVZ (Legacy) |
-| :--- | :--- | :--- | :--- | :--- |
-| **Architectures** | x86_64, ARM, AArch64, PPC64, s390, MIPS, RISC-V, LoongArch | x86, x86_64, ARM | x86, x86_64, PPC, ARM | x86, x86_64 |
-| **OS** | Linux | Linux | Linux | Linux |
-| **Standard Kernel?** | Yes (v3.11+) | Yes | Yes (needs module) | No (Custom kernel) |
-| **No Preloading?** | Yes | No | No | Yes |
-| **Non-Root Support?** | Yes (limited) | Yes | Yes | No |
-| **Unmodified Apps?** | Yes | Yes | No (Static/Threaded issues) | Yes |
-| **Unprepared Tasks?** | Yes | No | No | Yes |
-| **Retains Behavior?** | Yes | No (Wrappers used) | No (Wrappers used) | Yes |
-| **Live Migration** | Yes (Optimized) | Yes | Yes (Identical env only) | Yes |
-| **Containers** | Yes (LXC, Docker, Podman) | No | No | Yes |
-| **GDB Support** | No (same interface) | Yes | No | Yes |
-| **Unix Sockets** | Yes | Yes | No | Yes |
-| **TCP Sockets** | Yes | Yes | No | Yes |
-| **Established TCP** | Yes | No (needs plugin) | No | Yes |
-| **Namespaces** | Yes | No | No | Yes |
-| **System V IPC** | Yes | Yes | No | Yes |
-| **Non-POSIX Files** | Yes (Inotify, Epoll) | Yes | No | Yes |
-| **Timers** | Yes | No | Yes | Yes |
-
-## Sources and External Links
-
-* **DMTCP**: [dmtcp.sourceforge.net](http://dmtcp.sourceforge.net/)
-* **BLCR**: [ftg.lbl.gov/projects/CheckpointRestart](https://ftg.lbl.gov/projects/CheckpointRestart/)
-* **CRIU FAQ**: [How does DMTCP differ?](http://dmtcp.sourceforge.net/FAQ.html#Internals)
diff --git a/Documentation/under-the-hood/copy-on-write-memory.md b/Documentation/under-the-hood/copy-on-write-memory.md
deleted file mode 100644
index 5c95f7a8f..000000000
--- a/Documentation/under-the-hood/copy-on-write-memory.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copy-on-Write (COW) Memory Restoration
-
-CRIU employs a specialized multi-stage process to preserve Copy-on-Write (COW) sharing of private anonymous memory mappings during restoration. This prevents the memory duplication that would occur if each process's memory were restored independently, thereby significantly reducing the memory footprint of the restored process tree.
-
-## The Problem
-
-When a process calls `fork()`, the Linux kernel optimizes memory usage by sharing private anonymous mappings between the parent and child. Physical pages are only duplicated (COW) when one of the processes modifies them.
-
-Traditional checkpointing captures each process's memory separately. If restored naively (by mapping and filling each VMA individually), the kernel would allocate separate physical pages for the parent and child, even for pages that were originally shared. This leads to a massive increase in physical memory usage upon restoration.
-
-## CRIU's COW Restoration Strategy
-
-To keep COW mappings intact, CRIU performs restoration in a way that mimics the original `fork()` behavior.
-
-### 1. Identifying COW Candidates
-Before forking the process tree, CRIU analyzes the memory maps of all tasks:
-* It compares each task's VMAs with those of its parent.
-* Two VMAs are identified as COW candidates if they have identical start/end addresses, the same protection flags (e.g., `PROT_READ`, `PROT_WRITE`), and belong to the same executable.
-* This mapping is stored internally, marking which VMAs are "inherited" from a parent.
-
-### 2. Pre-mapping and Filling
-During restoration, processes are created in a specific order:
-1. **Root VMA Population**: If a VMA is the "root" of a COW set (it is not inherited), the restoring task maps it and fills it with data from the image files.
-2. **Inheritance via Fork**: When a task forks a child, the child automatically inherits the parent's memory mappings via the standard kernel COW mechanism.
-3. **Content Verification**: The child then iterates through its own memory images:
- * It compares the page contents in the image with the data already present in its inherited memory (which it got from the parent).
- * If the contents match exactly, the physical page remains shared with the parent.
- * If they differ (meaning the page was modified in either process after the original fork), the child overwrites the page with the data from its image, triggering a kernel COW event for that specific page.
-
-### 3. Cleaning Up (madvise)
-A parent may contain pages that were unmapped or modified in the child process. To ensure the child's memory layout is perfectly accurate:
-* CRIU maintains a bitmap of pages touched during the content verification stage.
-* After all pages are processed, CRIU uses `madvise(MADV_DONTNEED)` on any pages that exist in the inherited VMA but were not present in the child's dump images. This effectively "punches holes" in the child's VMA to match its original state while preserving the sharing of other pages.
-
-## Current Limitations
-
-* **Reparenting to Init**: If a process was reparented to the system `init` (PID 1) and that `init` process is not part of the checkpointed process tree, CRIU cannot identify the parent's VMAs, and COW sharing will not be restored for that process.
-* **VMA Movement**: If a VMA was moved (e.g., via `mremap`) after the original `fork()`, CRIU's current address-based matching algorithm will fail to identify it as a COW candidate.
-
-## See Also
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md)
-* [Restorer Context](restorer-context.md)
diff --git a/Documentation/under-the-hood/dmtcp.md b/Documentation/under-the-hood/dmtcp.md
deleted file mode 100644
index bd619d53d..000000000
--- a/Documentation/under-the-hood/dmtcp.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# DMTCP vs. CRIU
-
-This article explains the fundamental differences between CRIU and DMTCP (Distributed MultiThreaded Checkpointing), focusing on their architectural approaches to process capture and restoration.
-
-## Architectural Approach
-
-### DMTCP: Library-Level Interception
-DMTCP implements checkpoint/restore at the **userspace library level**. To use it, an application must be launched with the DMTCP library dynamically linked (`LD_PRELOAD`).
-* **Mechanism**: The library intercepts library calls (e.g., `glibc` wrappers for syscalls), builds an internal shadow database of the process state, and then forwards requests to the kernel.
-* **Implications**: This approach can introduce performance overhead due to proxying. Only applications compatible with the DMTCP library can be reliably dumped. Furthermore, DMTCP may not support all kernel APIs; for instance, complex features like `inotify` or specific socket types may lack sufficient proxies.
-
-### CRIU: Kernel-Level Integration
-CRIU, by contrast, operates primarily from **outside the process** using standard kernel interfaces (extended where necessary for C/R).
-* **Mechanism**: CRIU uses tools like `ptrace`, `/proc`, and specialized system calls (e.g., `kcmp`, `map_files`) to transparently capture the process state without requiring pre-loaded libraries.
-* **Implications**: It can checkpoint and restore virtually any application, provided the kernel supports the required features. It requires a relatively modern kernel version but offers much deeper integration with system resources like namespaces and cgroups.
-
-## PID Handling and Virtualization
-
-Restoring a process tree often requires restoring specific Process IDs (PIDs).
-
-* **DMTCP "Fake" PIDs**: Because the kernel does not traditionally allow userspace to request a specific PID during `fork()`, DMTCP "fools" the application. It intercepts the `getpid()` call and returns a fake value that matches the original PID. This is highly dangerous, as the application may see inconsistent information in the `/proc` filesystem (where directories are named by the *real* PID).
-* **CRIU Real PIDs**: CRIU restores the **actual PID** of the process. It achieves this by using the `ns_last_pid` interface or the modern `clone3` system call with a specified PID. This ensures that the restored process has the exact same identity as the original, with no inconsistencies in `/proc` or other kernel interfaces.
-
-## Summary
-
-DMTCP is often easier to deploy on older kernels since it doesn't require specific kernel support, but it suffers from the inherent limitations and risks of userspace interception. CRIU is the more robust and transparent solution for modern Linux systems, offering faithful restoration of the entire process environment.
diff --git a/Documentation/under-the-hood/dumping-files.md b/Documentation/under-the-hood/dumping-files.md
deleted file mode 100644
index 061ad6a52..000000000
--- a/Documentation/under-the-hood/dumping-files.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Dumping File Descriptors
-
-This document explains the internal mechanisms CRIU uses to capture the state of opened file descriptors (FDs).
-
-## Linux File Objects: Inodes, Dentries, and Files
-
-In the Linux kernel, an opened file is represented by a chain of three distinct objects:
-
-1. **Inode**: Contains metadata (owner, type, size) and pointers to the actual data on disk.
-2. **Dentry (Directory Entry)**: A helper object used to resolve file paths. An inode can have multiple dentries if hard links exist.
-3. **File (or "File Description")**: Represents an active handle to a dentry/inode pair. It maintains state such as the current file position (`pos`) and access flags.
-
-Crucially, **file descriptors** are per-task integers that point to these shared "File" objects. When a task calls `fork()`, the child's FDs point to the same "File" objects as the parent's.
-
-## How CRIU Collects FD Information
-
-Dumping FDs requires CRIU to collect state from both the kernel's `/proc` filesystem and the file objects themselves.
-
-### 1. Identifying Open FDs
-CRIU reads `/proc/$pid/fd/` and `/proc/$pid/fdinfo/` to determine which FD numbers are currently open and to retrieve their basic properties (position and flags).
-
-### 2. Retrieving File Objects (SCM_RIGHTS)
-To perform deeper inspection (like `fstat` or `ioctl`), CRIU needs a local copy of the file descriptor. It achieves this by:
-* Injecting **parasite code** into the target task.
-* Commanding the parasite to send the FDs to the CRIU coordinator via a Unix domain socket using the `SCM_RIGHTS` mechanism.
-
-### 3. Detecting Shared Files (gen_id and kcmp)
-To minimize image size and avoid redundant dumps, CRIU must identify if FDs in different tasks (or even the same task) point to the same underlying "File" object. It uses a two-stage optimization:
-1. **gen_id**: CRIU calculates a "generation ID" based on the file's device ID, inode number, and current position. If two FDs have different `gen_id`s, they are guaranteed to be different.
-2. **kcmp**: If `gen_id`s match, CRIU uses the `kcmp()` system call (with the `KCMP_FILE` flag) to definitively determine if the two descriptors refer to the same kernel "File" object.
-
-## Image Storage
-
-CRIU stores FD information in a two-tier structure:
-
-### The `fdinfo-$id.img` Image
-This per-task image maps task-specific FD numbers to global **File IDs**. Each entry contains:
-* `fd`: The numeric descriptor in the task.
-* `id`: A unique identifier for the underlying file object.
-
-### Specialized File Images
-The actual state of the file objects is stored in specialized images based on their type:
-* `reg-files.img`: Regular files (includes the path).
-* `pipes.img`: Pipes and FIFOs.
-* `unixsk.img` / `inetsk.img`: Sockets.
-* `signalfd.img`, `eventfd.img`, `epoll.img`, etc.
-
-This separation allows CRIU to efficiently handle shared files: multiple `fdinfo` entries can point to a single entry in a specialized file image.
-
-## See also
-
-* [Kcmp Trees](kcmp-trees.md)
-* [Parasite Code](parasite-code.md)
-* [Invisible Files](invisible-files.md)
diff --git a/Documentation/under-the-hood/faq.md b/Documentation/under-the-hood/faq.md
deleted file mode 100644
index 05ff19be7..000000000
--- a/Documentation/under-the-hood/faq.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Frequently Asked Questions (FAQ)
-
-This page provides answers to common questions and technical insights into CRIU's behavior and limitations.
-
-## General Questions
-
-### Q: Why does CRIU dump parts of read-only code mappings?
-**A**: Even if a mapping (like the code section of `/usr/bin/something`) is marked as read-only, it may still contain "dirty" pages that CRIU must dump. This typically happens due to **Copy-on-Write (COW)** events during dynamic linking, relocation patching, or if the process modified its own code via `mprotect` and `ptrace`.
-
-### Q: How can I verify that my system is ready for CRIU?
-**A**: Use the built-in check tool:
-```bash
-criu check --extra
-```
-This will verify that your kernel has all the necessary features (like `kcmp`, `ns_last_pid`, etc.) enabled. Additionally, running the [ZDTM Test Suite](zdtm-test-suite.md) is the best way to confirm functional correctness on your specific hardware and software stack.
-
-### Q: Is it possible to change the IP address during live migration?
-**A**: Yes, but with caveats. Since TCP connections are identified by their IP/Port 4-tuple, changing the IP will normally break established connections.
-- If you can tolerate connection resets, use the `--tcp-close` flag.
-- For listening sockets, you can use the `UPDATE_INETSK` plugin hook or the `CRIT` tool to remap addresses.
-- For seamless migration, virtual IPs or network-level NAT are required. See [Changing IP Addresses](change-ip-address.md) for more details.
-
-### Q: Why does restore fail with a "PID mismatch" error?
-**A**: This occurs because the PID CRIU is trying to restore is already in use by another process on the system.
-- **Solution**: The most common way to avoid this is to run the restored process inside a fresh **PID namespace**. This ensures that the PID range is entirely available to CRIU.
-- **Internal Note**: CRIU uses the `ns_last_pid` kernel interface or the modern `clone3` system call to request specific PIDs during restoration.
-
-### Q: Why does dump fail with "Cannot dump half of a stream unix connection"?
-**A**: This usually happens when one end of a Unix domain socket is held by a process *outside* the process tree being checkpointed. CRIU cannot capture the state of the "external" peer, so it cannot safely restore the connection unless the socket is explicitly marked as external via the `--external unix[ino]` option.
-
----
-
-## Testing (ZDTM)
-
-### Q: Why do my ZDTM tests fail with "Permission Denied" even when run as root?
-**A**: The `zdtm.py` test runner executes many sub-tests as a non-privileged user to verify CRIU's behavior in unprivileged environments. If your specific test requires root privileges, you must add `'flags': 'suid'` to the test's `.desc` file.
-
----
-
-## Containers and Docker
-
-### Q: Why can't I restore a Docker container onto a different image?
-**A**: CRIU checkpoints the state of the processes, but it does **not** checkpoint the underlying filesystem. The process images contain paths to files that must exist exactly as they did during the dump.
-- **Solution**: To restore a container, you must ensure the filesystem state is identical. In Docker, this often involves using `docker commit` to create an image of the container's filesystem at the moment of the checkpoint.
-- **Modern Tools**: Container engines like Podman or newer versions of Docker/RunC handle this integration more seamlessly by managing the filesystem snapshots alongside the CRIU state.
diff --git a/Documentation/under-the-hood/fdinfo-engine.md b/Documentation/under-the-hood/fdinfo-engine.md
deleted file mode 100644
index c37c43e86..000000000
--- a/Documentation/under-the-hood/fdinfo-engine.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# File Restoration Engine (fdinfo)
-
-CRIU uses a sophisticated state machine to restore file descriptors (FDs) across a process tree, handling shared files, complex dependencies, and inter-process synchronization.
-
-## Master and Slave Descriptors
-
-In the Linux kernel, multiple FDs can refer to the same underlying "File Description." CRIU mirrors this by categorizing FDs into **Masters** and **Slaves**:
-
-1. **Master**: For each unique file object, one FD is designated as the master. This process is responsible for the actual `open()`, `socket()`, or `pipe()` system call that recreates the object.
-2. **Slaves**: All other FDs referring to the same object are slaves. They do not perform the creation call themselves; instead, they receive the file descriptor from the master.
-3. **Transport (SCM_RIGHTS)**: CRIU uses Unix domain sockets and the `SCM_RIGHTS` mechanism to "send" file descriptors from the master process to slave processes.
-
-## Per-Process Restore Loop
-
-Each task in the process tree executes a loop (`open_fdinfos`) to restore its descriptors. The core of this loop is the `file_desc_ops->open()` method.
-
-### The `open()` State Machine
-The `open()` method for a master file can return one of three values:
-* **0 (Success)**: The file is fully restored.
-* **1 (In Progress)**: The file has been opened (or the process has started opening it), but it cannot be completed yet due to a dependency on another file. The loop will call this method again in the next iteration.
-* **-1 (Failure)**: An error occurred, and restoration must abort.
-
-### Early FD Distribution
-To maximize parallelism, a master can return a valid FD in the `new_fd` argument even if it returns `1` (In Progress). This allows CRIU to immediately distribute the FD to all slave processes via `SCM_RIGHTS`, even before the master has finished its own restoration steps (e.g., a connected Unix socket waiting for its peer).
-
-## Inter-Process Synchronization
-
-CRIU uses **futexes** and a specialized event mechanism to coordinate between processes:
-* **set_fds_event(pid)**: Signals a task that a file it was waiting for (as a slave) is now available or that a dependency has changed.
-* **wait_fds_event()**: Causes a task to sleep until it receives a notification.
-* **FLE Stages**: Each descriptor entry (`struct fdinfo_list_entry` or `fle`) transitions through stages: `INITIALIZED` -> `OPEN` -> `RESTORED`.
-
-## Key Dependencies
-
-The engine must resolve complex dependencies between different file types:
-1. **TTYs**: A slave TTY can only be fully restored after its master peer is active.
-2. **Unix Sockets**: A connected socket must wait for its peer to `bind()` to its address before it can `connect()`.
-3. **Epoll**: An epoll FD can be created immediately, but adding FDs to its interest list must wait until those FDs are themselves restored.
-4. **Pipes and Socketpairs**: These calls create two FDs at once. One is treated as the primary master, and the second is distributed to the appropriate task (which might be the same task or a different one).
-
-## Technical Notes
-
-* **Service FDs**: CRIU maintains its own internal FDs (for images, logs, etc.) in a "protected" range to avoid conflicts with the application's FDs during restoration.
-* **Ordering**: Descriptors are generally restored in ascending order of their FD number to improve efficiency, though dependencies can override this order.
diff --git a/Documentation/under-the-hood/filesystems-pecularities.md b/Documentation/under-the-hood/filesystems-pecularities.md
deleted file mode 100644
index 41dd865f7..000000000
--- a/Documentation/under-the-hood/filesystems-pecularities.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Filesystem Peculiarities in CRIU
-
-While Linux aims for a uniform filesystem interface, several filesystems have unique behaviors ("peculiarities") that require specialized handling in CRIU to ensure accurate checkpointing and restoration.
-
-## BTRFS: Virtual Device Numbers
-
-When you `stat()` a file on BTRFS, the kernel often reports a **virtual device ID** (`st_dev`) that is unique to that specific subvolume or snapshot. However, other kernel interfaces, such as `/proc/$pid/mountinfo` or the `sock_diag` subsystem, may report the **physical device ID**.
-
-**Problem**: CRIU cannot rely on simple `st_dev` comparisons to identify which mount a file belongs to, as the virtual and physical IDs will mismatch.
-
-**Solution**: CRIU performs userspace path-to-device resolution. It analyzes `/proc/$pid/mountinfo` to build a mapping between virtual and physical IDs, allowing it to correctly resolve file locations. See `mount.c:phys_stat_resolve_dev()`.
-
-**Workaround**: In some environments (like Podman), disabling Copy-on-Write for the container storage (`chattr +C`) can mitigate some BTRFS-related complexities.
-
-## NFS: "Silly Rename" and Unlinked Files
-
-NFS handles unlinked but open files differently than local filesystems. When a file is unlinked while still open, the NFS client performs a **"Silly Rename"**, renaming the file to something like `.nfsXXX` instead of truly removing it.
-
-**Problem**: CRIU's standard logic for detecting unlinked files (checking if `st_nlink == 0`) fails on NFS because the "silly renamed" file still has a link count of 1.
-
-**Solution**: CRIU explicitly checks if a file resides on an NFS mount. If it does, it examines the filename for the `.nfs` prefix. If both conditions match, CRIU treats the file as "opened and unlinked," capturing its contents into the image as a **ghost file**. See `files-reg.c:nfs_silly_rename()`.
-
-## OverlayFS: Path Inconsistencies and Link-Remap
-
-OverlayFS, the standard for modern container engines, has several known issues:
-
-1. **Path Mismatches (Pre-v4.2)**: On older kernels, `/proc/$pid/fd/` and `/proc/$pid/fdinfo/` could report paths that did not include the OverlayFS mountpoint. CRIU detects OverlayFS mounts and manually corrects these paths using information from the mount table.
-2. **linkat() Failures**: In OverlayFS, the `linkat()` system call fails with `ENOENT` if the file being linked resides on a **lower layer** (read-only layer) and has been unlinked from the upper layer.
- * **CRIU Response**: When a "link-remap" (linking a deleted file back to the filesystem) fails on OverlayFS, CRIU automatically falls back to dumping the file as a **ghost file** (copying its contents into the image).
-
-## AUFS: Branch Path Leakage (Legacy)
-
-AUFS (mostly superseded by OverlayFS) has a bug where `/proc/$pid/maps` reveals the path of a file within its internal **branch** directory instead of its visible path within the AUFS mount.
-
-**Solution**: CRIU identifies AUFS mounts, reads the branch configuration from `sysfs`, and "fixes" the paths in the memory map to ensure the file can be correctly located during restoration. See `sysfs_parse.c:fixup_aufs_vma_fd`.
-
-## See also
-* [How Hard is it to Open a File?](how-hard-is-it-to-open-a-file.md)
-* [Invisible Files](invisible-files.md)
-* [Mount Points](mount-points.md)
diff --git a/Documentation/under-the-hood/final-states.md b/Documentation/under-the-hood/final-states.md
deleted file mode 100644
index 5dd581b1f..000000000
--- a/Documentation/under-the-hood/final-states.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Process Tree Final States
-
-This document describes the possible states a process tree can end up in after a successful CRIU **dump** or **restore** operation.
-
-## Supported Final States
-
-CRIU supports three primary final states for the process tree:
-
-1. **Running (`TASK_ALIVE`)**: The processes continue execution as normal.
-2. **Stopped (`TASK_STOPPED`)**: The processes are left in a stopped state (equivalent to receiving `SIGSTOP`).
-3. **Dead (`TASK_DEAD`)**: The processes are terminated (equivalent to receiving `SIGKILL`).
-
-## Controlling the Final State
-
-You can specify the desired final state using the following command-line options:
-
-* `--leave-running`: Forces the process tree to continue running after the operation.
-* `--leave-stopped`: Forces the process tree to remain stopped after the operation.
-
-### Default Behavior for `criu dump`
-
-By default, `criu dump` terminates the process tree (**Dead**).
-
-**Rationale**: Leaving a process tree running after a full dump is risky. If the processes continue to run, they will likely modify the filesystem, network state, or shared memory. These changes can make the captured image inconsistent or impossible to restore later, as the system state will no longer match the process's internal state at the moment of the dump.
-
-* **Exceptions**: The `pre-dump` command always enforces the **Running** state, as its purpose is to capture memory changes while the application continues to operate.
-
-### Default Behavior for `criu restore`
-
-By default, `criu restore` resumes the process tree (**Running**).
-
-**Rationale**: The primary goal of restoration is typically to resume the application's work immediately.
-
-* **Debugging**: Using `--leave-stopped` during restoration can be extremely useful for debugging. It allows you to inspect the restored process tree (e.g., via `/proc` or a debugger) before it begins executing any code.
-
-## Resuming a Stopped Tree
-
-If a process tree was left in the **Stopped** state (either by dump or restore), you can resume its execution by sending a `SIGCONT` signal to all processes in the tree.
-
-For complex process trees, the [pstree_cont.py](https://github.com/checkpoint-restore/criu-scripts/blob/master/pstree_cont.py) script (available in the `criu-scripts` repository) can be used to safely resume the entire hierarchy by targeting the root PID.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Freezing the Tree](freezing-the-tree.md)
diff --git a/Documentation/under-the-hood/freezing-the-tree.md b/Documentation/under-the-hood/freezing-the-tree.md
deleted file mode 100644
index c8c014e99..000000000
--- a/Documentation/under-the-hood/freezing-the-tree.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Freezing the Process Tree
-
-Before CRIU can begin checkpointing, it must ensure that the entire process tree is completely "immobilized." This prevents tasks from changing their state (e.g., opening files, creating children, or receiving network packets) while the snapshot is being taken. This freezing process must be transparent to the application, meaning it should not observe any disruption or unexpected signals.
-
-CRIU employs two primary methods to achieve this:
-
-## Capturing with ptrace
-
-The most common method for freezing a tree is using the Linux `ptrace` interface. Unlike traditional debuggers that might send disruptive signals like `SIGSTOP`, CRIU uses a more modern, non-invasive approach:
-
-1. **SEIZE**: CRIU calls `ptrace(PTRACE_SEIZE, pid, ...)` for every task in the tree. This "attaches" to the process without stopping it or delivering any signals.
-2. **INTERRUPT**: Once seized, CRIU sends a `ptrace(PTRACE_INTERRUPT, pid, ...)` command. This causes the kernel to stop the task at the next possible opportunity (typically upon entering or exiting a syscall or being preempted).
-3. **WAIT**: CRIU then waits for the task to enter the `TRAP_STOP` state. This state is invisible to the task's own signal handling logic, ensuring transparency.
-
-By seizing every task in the tree, CRIU ensures that no task can resume execution or fork new children during the dump.
-
-## Using Freezer CGroups
-
-For large process trees or environments where `ptrace` might be restricted or inefficient, CRIU can use the Linux **Freezer CGroup**. This allows the kernel to freeze an entire group of processes in a single, atomic operation.
-
-CRIU supports both versions of the freezer:
-
-### CGroup v1 Freezer
-* **Mechanism**: CRIU identifies the freezer cgroup containing the process tree and writes `FROZEN` to the `freezer.state` file.
-* **Handling Inconsistency**: Historically, the v1 freezer could be unreliable, sometimes getting stuck in a `FREEZING` state. CRIU includes "kludges" to handle this, such as periodically retrying the freeze command or briefly thawing and re-freezing the group to kick the kernel's internal state machine.
-
-### CGroup v2 Freezer
-* **Mechanism**: In the unified cgroup v2 hierarchy, CRIU writes `1` to the `cgroup.freeze` file.
-* **Verification**: It then monitors the `cgroup.events` file, waiting for the `frozen 1` event to signal that all processes in the sub-hierarchy have successfully stopped.
-
-**Note**: Even when using a freezer cgroup, CRIU still attaches to the tasks via `ptrace` after they are frozen. This is necessary to perform internal inspections, such as extracting register states and injecting parasite code.
-
-## See also
-
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Process Tree Final States](final-states.md)
-* [Parasite Code](parasite-code.md)
diff --git a/Documentation/under-the-hood/fsnotify.md b/Documentation/under-the-hood/fsnotify.md
deleted file mode 100644
index 1b5cdd80b..000000000
--- a/Documentation/under-the-hood/fsnotify.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# FSNotify (Inotify and Fanotify)
-
-CRIU supports checkpointing and restoring `inotify` and `fanotify` instances. These mechanisms allow applications to monitor filesystem events (like file creation, modification, or deletion).
-
-## The Challenges of FSNotify C/R
-
-Restoring an fsnotify instance is inherently difficult because the kernel does not provide a direct way to retrieve the original path of a watched object (the "watchee"). Furthermore, the event queues themselves pose consistency risks.
-
-### 1. Identifying the Watchee
-When an application adds a watch (via `inotify_add_watch`), the kernel associates the watch with an **inode**, but it does not store the **path** used to create it. To restore the watch, CRIU must find a valid path to that specific inode.
-* **Open by Handle**: CRIU first attempts to use `open_by_handle_at()`. If the filesystem supports file handles, CRIU captures the handle during the dump and uses it to re-open the inode during restoration without needing the original path.
-* **Irmap (Inode Reverse Mapping)**: If file handles are unavailable, CRIU uses the [Irmap](irmap.md) engine to scan the filesystem and find a path that leads to the target inode.
-
-### 2. Event Queue Consistency
-If there are pending events in the fsnotify queue at the time of the dump, CRIU cannot currently "peek" at them or safely migrate them.
-* **Dropped Events**: During a dump, CRIU checks if the fsnotify file descriptor has data. If it does, CRIU emits a warning: `The ... inotify events will be dropped`. These events are lost, and the application must be prepared to handle this gap in its event stream.
-* **Spurious Events**: The process of checkpointing and restoring itself can trigger new filesystem events. For example, creating or deleting **ghost files** (temporary files used to restore unlinked but open files) can generate `IN_CREATE` or `IN_DELETE` events that the application will see upon resumption.
-
-### 3. Ghost Files and Circular Dependencies
-A "ghost file" is a file that was deleted by the application but is still held open. During restoration, CRIU must recreate these files. This action itself generates notify events, potentially confusing applications that monitor the directories where these ghost files are temporarily placed.
-
-## Support for Fanotify
-
-CRIU also supports `fanotify`, including:
-* **Inode Marks**: Similar to inotify, these target specific files or directories.
-* **Mount Marks**: Fanotify can monitor entire mount points. CRIU identifies the mount ID and restores the mark on the corresponding mount in the restored namespace.
-
-## Current Strategy: "Chopping the Knot"
-
-Due to the complexity of perfectly migrating event queues, CRIU's current strategy is:
-1. **Warn and Drop**: Acknowledge that pending events are lost.
-2. **Restore the Watches**: Ensure the application continues to receive *new* events after restoration.
-3. **Namespace Integration**: Correctly map mount-level fanotify marks within their respective mount namespaces.
-
-## See also
-* [Irmap](irmap.md)
-* [Invisible Files](invisible-files.md)
-* [Mount Points](mount-points.md)
diff --git a/Documentation/under-the-hood/how-hard-is-it-to-open-a-file.md b/Documentation/under-the-hood/how-hard-is-it-to-open-a-file.md
deleted file mode 100644
index f13bf32e1..000000000
--- a/Documentation/under-the-hood/how-hard-is-it-to-open-a-file.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# The Complexity of Re-opening Files during Restore
-
-Re-creating an open file descriptor during restoration is far more complex than simply calling `open(path, flags)`. This article explores the numerous edge cases CRIU must handle to faithfully reconstruct the file state.
-
-## 1. Basic Opening
-
-At its simplest, a file is defined by its path and access mode:
-```c
-int fd = open(f->path, f->mode);
-```
-However, this is only the beginning of the process.
-
-## 2. FIFOs and Blocking
-A standard `open()` call on a FIFO (named pipe) can hang indefinitely if there is no corresponding reader or writer on the other end. CRIU avoids this by first opening the FIFO with `O_RDWR` (to ensure at least one of each is present) and then using `dup2` to establish the final descriptor with the correct original flags.
-
-## 3. Unlinked but Open Files (Ghost Files)
-Linux allows files to be deleted while they are still open. These "invisible" files no longer have a path in the filesystem.
-* **link-remap**: If the file still has other hard links elsewhere, CRIU may create a temporary link to it to allow it to be re-opened via a path.
-* **Ghost Files**: If the link count is zero, CRIU captures the entire content of the file during the dump. During restore, it recreates this file in a temporary location, opens it, and then unlinks it to match the original state.
-
-## 4. Directories and Hard Links
-Directories cannot be hard-linked. If a directory was unlinked, CRIU must recreate it, open it, and then remove it. For files with multiple hard links that were all deleted, CRIU must ensure they all point back to the same physical inode upon restoration, requiring careful tracking of "temporary" paths and user-space reference counts.
-
-## 5. Mount Namespaces and Chroot
-The same path (e.g., `/etc/passwd`) might refer to entirely different files depending on the mount namespace or `chroot` environment of the process.
-* **mnt_id**: CRIU records the mount ID for every file during the dump.
-* **open_ns_root**: During restoration, CRIU uses file descriptors referring to the root of the specific mount namespace to ensure that `openat()` targets the correct physical file, regardless of the restorer's current root.
-
-## 6. File Ownership and Signals (fown)
-Files can have an associated "owner" (a PID or PGID) that receives signals (like `SIGIO` or `SIGPOLL`) when I/O events occur.
-* **F_SETOWN_EX**: CRIU restores this ownership using the extended owner structure.
-* **UID Switching**: Setting the owner of a file may require specific privileges. CRIU may temporarily switch its effective UIDs during the `fcntl` call to satisfy kernel permission checks if the file owner differs from the restorer.
-* **F_SETSIG**: The specific signal number to be delivered is also faithfully restored.
-
-## 7. Position and Flags
-* **Lseek**: The current byte offset (`pos`) is restored using `lseek`.
-* **Flag Sanitization**: Certain flags (like `O_CREAT`, `O_EXCL`, `O_TRUNC`) only make sense during the initial creation of a file. CRIU strips these before the restore-time `open()` to avoid accidentally creating or truncating existing files.
-* **O_PATH**: Files opened with `O_PATH` are handled as pure path references; they do not have positions, ownership, or data access.
-
-## 8. The Final Step: Descriptor Planting
-Once a file is successfully opened (at a temporary descriptor number assigned by the kernel), it must be moved to the exact numeric descriptor the application expects (e.g., FD 42). This is achieved via `dup2()`, but requires coordination when descriptors are shared across a process tree.
-
-*See also: [How to assign needed file descriptor to a file](how-to-assign-needed-file-descriptor-to-a-file.md)*
diff --git a/Documentation/under-the-hood/how-to-assign-needed-file-descriptor-to-a-file.md b/Documentation/under-the-hood/how-to-assign-needed-file-descriptor-to-a-file.md
deleted file mode 100644
index 0352f925f..000000000
--- a/Documentation/under-the-hood/how-to-assign-needed-file-descriptor-to-a-file.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Assigning Descriptors and Sharing Files
-
-Once a file is [opened during restoration](how-hard-is-it-to-open-a-file.md), it often needs to be moved to a specific numeric file descriptor (FD) and potentially shared with other tasks in the process tree. This document explains how CRIU coordinates this process.
-
-## The Basic Mechanism: `dup2`
-
-In Linux, the `dup2(oldfd, newfd)` system call is the standard way to assign a file to a specific descriptor number. CRIU uses this to move a newly opened file from its temporary descriptor (assigned by the kernel) to the target descriptor expected by the application.
-
-```c
-int fd = open_a_file(f->file);
-dup2(fd, f->target_fd);
-close(fd);
-```
-
-## Handling Multiple Descriptors for One File
-
-A single task may have multiple FDs referring to the same kernel "File Description" (e.g., a shell where FD 0, 1, and 2 all point to the same TTY). CRIU handles this by identifying the unique file object, opening it once, and then calling `dup2()` for every target FD slot the application expects.
-
-## Sharing Files Across the Process Tree
-
-Files are frequently shared between processes. While these files were originally inherited via `fork()`, CRIU must often distribute them between processes that do not have a direct parent-child relationship during the restore phase.
-
-### Master and Slave Descriptors
-For every unique file object in a checkpoint:
-1. **The Master**: One task is designated as the "master" for that file. It is responsible for the actual system call that recreates the object (e.g., `open()`, `socket()`, or `pipe()`).
-2. **The Slaves**: All other tasks that share the same file are "slaves." They do not create the file themselves.
-
-### Transport via SCM_RIGHTS
-CRIU uses Unix domain sockets to "send" descriptors from the master process to slave processes using the `SCM_RIGHTS` mechanism.
-
-**The Workflow:**
-1. **Master Opens**: The master task creates the file object.
-2. **Master Sends**: The master sends the resulting file descriptor to each slave task over a dedicated transport socket.
-3. **Slave Receives**: The slave task waits on its transport socket, receives the FD, and uses `dup2()` to plant it into the correct numeric slot.
-
-## Solving the Coordination Problem
-
-Distributing thousands of descriptors across a complex process tree requires careful management to avoid deadlocks and descriptor collisions.
-
-### 1. Transport Sockets
-CRIU creates abstract Unix sockets for each process to receive descriptors. The names are uniquely generated using the PID and a `criu_run_id` (e.g., `\0x/crtools-fd-123-abcdef`) to ensure that multiple simultaneous CRIU runs on the same host do not interfere with each other.
-
-### 2. Deterministic "Master" Selection
-To prevent circular dependencies (e.g., Task A waiting for Task B while B waits for A), CRIU uses a deterministic priority system to select the master. Typically, the task with the highest priority—usually the one closest to the root of the tree or with the lowest PID—is chosen to open and distribute the file.
-
-### 3. Descriptor Collisions
-A task's target FDs may conflict with the internal "service" FDs CRIU uses for images, logs, or transport sockets. CRIU resolves this by:
-* **Service FD Range**: Restricting CRIU's own FDs to a specific range.
-* **Dynamic Relocation**: If a target FD slot is occupied by an active service FD, CRIU moves the service FD to a new, free slot using `dup()` before planting the application's FD.
-
-## Complex Dependencies
-
-Some file types have inherent dependencies. For instance, an `epoll` descriptor cannot be fully restored until the files it monitors are already opened and their numeric descriptors are known. CRIU's file restoration engine handles this via a multi-pass state machine, where some files are opened but their full restoration is deferred until their dependencies are satisfied.
-
-*See also: [File Restoration Engine (fdinfo)](fdinfo-engine.md)*
diff --git a/Documentation/under-the-hood/how-to-open-a-file-without-open-system-call.md b/Documentation/under-the-hood/how-to-open-a-file-without-open-system-call.md
deleted file mode 100644
index 54ae43c6e..000000000
--- a/Documentation/under-the-hood/how-to-open-a-file-without-open-system-call.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Re-opening Files without Paths (open_by_handle_at)
-
-Occasionally, CRIU encounters an open file descriptor for which the kernel no longer maintains a path. This document explains how CRIU uses file handles and Inode Reverse Mapping (Irmap) to reconstruct these "nameless" files.
-
-## When Paths Are Lost
-
-The most common scenario for path loss occurs with **fsnotify** (inotify and fanotify) instances.
-When an application calls `inotify_add_watch(path)`, the kernel:
-1. Resolves the path to an **inode**.
-2. Attaches a watch generator to that inode.
-3. Immediately forgets the path used to create the watch.
-
-The resulting file descriptor points to the fsnotify instance, which knows *which* inode it is watching but not *where* that inode lives in the filesystem hierarchy. Because the dentry (directory entry) cache can be shrunk by the kernel at any time, the path information is often permanently lost to userspace.
-
-## Strategy 1: open_by_handle_at
-
-Linux provides a specialized system call, `open_by_handle_at()`, designed for userspace NFS servers. It allows opening a file using a **File Handle**—a filesystem-specific blob of bytes that uniquely identifies an inode.
-
-### The Handle mechanism
-1. **Dumping**: CRIU reads the file handle for a watch from `/proc/$pid/fdinfo/$fd`. (CRIU developers upstreamed patches to the Linux kernel to ensure this information is exposed).
-2. **Restoring**: During restoration, CRIU takes this handle and calls `open_by_handle_at()`. This returns an `O_PATH` file descriptor pointing to the original inode, even if its original path is unknown.
-3. **Re-attaching**: CRIU then uses this `O_PATH` descriptor to re-establish the inotify or fanotify watch, effectively "tricking" the kernel into watching the correct inode.
-
-## Strategy 2: Irmap (Inode Reverse Mapping)
-
-Not all filesystems support file handles (e.g., some older or specialized filesystems). In these cases, CRIU must resort to a brute-force approach called **Irmap**.
-
-The Irmap engine maintains a cache that maps `(device, inode)` pairs back to their filesystem paths.
-1. **Scanning**: Irmap recursively scans "known" directories (like configuration paths or application homes) and records every name-to-inode mapping it finds.
-2. **Lookup**: When CRIU needs a path for a specific inode, it queries the Irmap cache.
-3. **Pre-dump Integration**: To minimize the performance impact of filesystem scanning, CRIU can perform this scan during a **pre-dump** while the application is still running. The results are saved to an `irmap-cache.img` file and reused during the final dump.
-
-## Filesystem Specifics
-
-* **Tmpfs**: This filesystem pins its dentries in memory. For tmpfs, paths are almost always available via `/proc` and do not require handles or Irmap.
-* **OverlayFS**: Due to its layered nature, OverlayFS can have complex handle behaviors. CRIU includes specific logic to navigate these layers during handle resolution.
-
-## See also
-* [Dumping File Descriptors](dumping-files.md)
-* [FSNotify (Inotify and Fanotify)](fsnotify.md)
-* [Irmap](irmap.md)
diff --git a/Documentation/under-the-hood/index.md b/Documentation/under-the-hood/index.md
deleted file mode 100644
index 37870ba67..000000000
--- a/Documentation/under-the-hood/index.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# CRIU: Under the Hood
-
-This directory contains technical documentation detailing the internal implementation of CRIU, the kernel APIs it leverages, and the complex algorithms used to achieve checkpoint and restore of various Linux resources.
-
-## Core Architecture & Lifecycle
-
-* [Checkpoint and Restore Overview](checkpointrestore.md): High-level view of the C/R process.
-* [Freezing the Process Tree](freezing-the-tree.md): How CRIU stops the application using the freezer cgroup or signals.
-* [Parasite Code](parasite-code.md): Injection and execution of code within the victim's address space.
-* [Restorer Context](restorer-context.md): The environment in which the restorer blob executes.
-* [Stages of Restore](stages-of-restoring.md): Detailed breakdown of the multi-stage restoration process.
-* [Final States](final-states.md): The state of processes after restore.
-* [Technologies Used](technologies.md): Overview of kernel technologies CRIU depends on.
-* [Kerndat](kerndat.md): How CRIU probes and caches kernel feature support.
-
-## Memory Management
-
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md): The primary algorithms for memory C/R.
-* [Memory Changes Tracking](memory-changes-tracking.md): Using dirty bits (soft-dirty) for iterative migration.
-* [Pagemap Cache](pagemap-cache.md): Optimizing access to `/proc/pid/pagemap`.
-* [Copy-on-Write Memory](copy-on-write-memory.md): Handling shared and private COW mappings.
-* [Shared Memory](shared-memory.md): Restoration of SysV IPC and POSIX shared memory.
-* [Memory Images Deduplication](memory-images-deduplication.md): Saving space in image files.
-* [Optimizing Pre-dump Algorithm](optimizing-pre-dump-algorithm.md): Strategies for minimizing downtime.
-* [Userfaultfd](userfaultfd.md): Lazy migration and post-copy restoration.
-
-## Files, Mounts & I/O
-
-* [Dumping Files](dumping-files.md): General overview of file descriptor C/R.
-* [How hard is it to open a file?](how-hard-is-it-to-open-a-file.md): The complexities of reconstructing file states.
-* [How to open a file without open() syscall](how-to-open-a-file-without-open-system-call.md): Using `linkat` and other tricks for inaccessible files.
-* [How to assign needed FD to a file](how-to-assign-needed-file-descriptor-to-a-file.md): Re-mapping FDs to match original values.
-* [Invisible Files](invisible-files.md): Handling unlinked but open files.
-* [FD Info Engine](fdinfo-engine.md): Parsing `/proc/pid/fdinfo`.
-* [Service Descriptors](service-descriptors.md): Managing CRIU's internal FDs to avoid collisions.
-* [Mount Points](mount-points.md): Basic mount restoration.
-* [Mount V2](mount-v2.md): Modern mount restoration using `open_tree` and `move_mount`.
-* [Mounts V2 Virtuozzo](mounts-v2-virtuozzo.md): Extensions for Virtuozzo-specific mount features.
-* [Filesystem Peculiarities](filesystems-pecularities.md): Handling `/dev`, `/proc`, `sysfs`, etc.
-* [IRM](irmap.md): Inode-to-path mapping (irmap).
-* [KCMP Trees](kcmp-trees.md): Using `kcmp` to deduplicate shared resources.
-* [Validate Files on Restore](validate-files-on-restore.md): Ensuring file consistency.
-* [FSNotify](fsnotify.md): Checkpointing inotify and fanotify marks.
-* [AIO](aio.md): Checkpointing asynchronous I/O contexts.
-
-## Networking
-
-* [TCP Connections](tcp-connection.md): Using TCP Repair mode for zero-loss socket migration.
-* [Unix Sockets](unix-sockets.md): Reconnecting stream and dgram unix sockets.
-* [Sockets](sockets.md): General socket restoration (Netlink, Raw, etc.).
-* [Change IP Address](change-ip-address.md): Handling network configuration changes during migration.
-* [MAC-VLAN](mac-vlan.md): Support for MAC-VLAN interfaces.
-* [TUN/TAP](tun-tap.md): Virtual network device restoration.
-
-## Process & Resource Management
-
-* [PID Restore](pid-restore.md): Algorithms for restoring tasks with specific PIDs.
-* [PIDFD](pidfd.md): Checkpointing and restoring pidfds.
-* [PIDFD Store](pidfd-store.md): Internal management of pidfds.
-* [Zombies](zombies.md): Handling processes in the `EXIT_ZOMBIE` state.
-* [32-bit Tasks C/R](32bit-tasks-cr.md): Specifics for IA32/compat mode tasks.
-* [Pending Signals](pending-signals.md): Capturing and re-queuing signals.
-* [Restartable Sequences (rseq)](restartable-sequences.md): Handling the `rseq` kernel feature.
-* [vDSO](vdso.md): Handling the virtual dynamic shared object across kernel versions.
-* [TTYs](ttys.md): The complex state of terminal devices and PTY pairs.
-* [CGroups](cgroups.md): Restoring cgroup hierarchy and membership.
-
-## Security & Kernel Features
-
-* [AppArmor](apparmor.md): Handling AppArmor profiles during dump and restore.
-* [ARM64 GCS](arm64-gcs.md): Guarded Control Stack support on AArch64.
-* [BPF Maps](bpf-maps.md): Experimental support for checkpointing BPF map data.
-* [Code Blobs](code-blobs.md): Management of PIE blobs (parasite, restorer).
-
-## Comparison & External Tools
-
-* [Comparison to other C/R projects](comparison-to-other-cr-projects.md): How CRIU differs from DMTCP, BLCR, etc.
-* [DMTCP](dmtcp.md): Specific notes on DMTCP integration or comparison.
-* [FAQ](faq.md): Frequently Asked Questions about CRIU internals.
-
----
-*Generated by Gemini CLI as part of the Documentation Audit (March 2026).*
diff --git a/Documentation/under-the-hood/invisible-files.md b/Documentation/under-the-hood/invisible-files.md
deleted file mode 100644
index 8ded713de..000000000
--- a/Documentation/under-the-hood/invisible-files.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# Invisible and Nameless Files
-
-In Linux, a file can remain accessible to a process even if it no longer has a visible path in the filesystem. This occurs when a file is unlinked (deleted) while still open or when its path becomes inaccessible due to mount shadowing. This document explains how CRIU detects and reconstructs these "invisible" files.
-
-## How Files Lose Their Paths
-
-### 1. Unlinked while Open
-The most common case is when an application opens a file and then immediately deletes it:
-```c
-int fd = open("/tmp/secret", O_RDWR);
-unlink("/tmp/secret");
-```
-The file data persists in the kernel as long as the file descriptor remains open, but it no longer exists in the filesystem directory structure.
-
-### 2. Virtual Filesystem Deletion
-On virtual filesystems like `/proc`, if a process dies, its entries (e.g., `/proc/$PID/cmdline`) disappear. However, if another process still has an open file descriptor to one of these entries, the file remains alive but "nameless."
-
-### 3. Mount Shadowing (Overmounts)
-If a process opens a file in `/mnt/data` and then a new filesystem is mounted over `/mnt`, the original file becomes inaccessible via its path.
-
-## CRIU's Detection and Reconstruction Strategies
-
-CRIU uses the `/proc/$pid/fd/` and `/proc/$pid/fdinfo/` interfaces to identify open files and their expected paths.
-
-### Ghost Files (Link Count = 0)
-If a file has a link count of zero (`st_nlink == 0`), it is truly deleted.
-* **Dumping**: CRIU reads the entire content of the file and stores it within the image directory as a "ghost file."
-* **Restoring**: During restoration, CRIU recreates the file in a temporary location, opens it, and then immediately unlinks it to restore the original unlinked state.
-* **Optimization**: For large sparse files, CRIU can use the `--ghost-fiemap` option to only capture the data blocks, significantly reducing image size.
-
-### Link-Remap (Link Count > 0)
-If a file has a positive link count but its expected path is missing or points to a different file, it means the specific name used to open the file was deleted, but other hard links still exist.
-* **Strategy**: CRIU uses `linkat()` with the `AT_EMPTY_PATH` flag to create a temporary name for the file on the same filesystem. This allows it to be re-opened via a path during restoration.
-* **Option**: This behavior is enabled via the `--link-remap` flag.
-
-### Virtual File Remap (The PID Helper)
-For deleted `/proc` entries, CRIU cannot use ghost files or `linkat()`. Instead:
-1. It records the PID of the original process that the `/proc` entry referred to.
-2. During restoration, it creates a temporary **TASK_HELPER** process with that specific PID.
-3. The restored application opens the `/proc/$PID/...` entry of this helper.
-4. The helper is terminated once all restoration tasks are complete.
-
-### Filesystem-Specific Handling
-
-* **NFS**: CRIU detects "Silly Rename" files (`.nfsXXX`) and handles them via the link-remap mechanism.
-* **OverlayFS**: Since `linkat()` may fail on OverlayFS if the file resides on a read-only lower layer, CRIU automatically falls back to the ghost file strategy in these cases.
-* **Devpts**: Files on `devpts` (like PTYs) are managed by the kernel and are restored using specific PTY master/slave allocation logic rather than file-based reconstruction.
-
-## Technical Details
-
-* **--ghost-limit**: By default, CRIU limits ghost files to **1 MB** to prevent excessive disk usage. This can be increased via the `--ghost-limit` option.
-* **--evasive-devices**: Allows CRIU to proceed even if a character or block device path has changed, provided the device numbers (`st_rdev`) match.
-
-## See also
-* [Dumping File Descriptors](dumping-files.md)
-* [Filesystem Peculiarities](filesystems-pecularities.md)
-* [Mount Points](mount-points.md)
diff --git a/Documentation/under-the-hood/irmap.md b/Documentation/under-the-hood/irmap.md
deleted file mode 100644
index 639652fce..000000000
--- a/Documentation/under-the-hood/irmap.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Irmap (Inode Reverse Mapping)
-
-Irmap is CRIU's engine for resolving an `(inode, device)` pair back into a filesystem path. This is primarily required for restoring **fsnotify** (inotify and fanotify) instances, which internally reference inodes but do not preserve the paths used to create them.
-
-## The Problem
-
-When an application creates an inotify watch, the kernel resolves the path to an inode and attaches the watch to it. The original path string is then discarded by the kernel. During a checkpoint, CRIU can see which inode is being watched but needs a valid path to recreate that watch during restoration.
-
-## How Irmap Works
-
-Irmap uses a combination of predefined hints and brute-force scanning to build a reverse mapping cache.
-
-### 1. Heuristic Hints
-CRIU starts by scanning "known" locations where applications typically place watches, such as:
-- `/etc` (configuration files)
-- `/var/log` (log monitoring)
-- `/var/spool`
-- D-Bus and Polkit service paths (`/usr/share/dbus-1/services`, etc.)
-- `/lib/udev`
-- The root directory (`/`)
-
-### 2. User-Defined Scan Paths
-Users can provide additional directories to scan via the command line to help CRIU find application-specific files more quickly:
-```bash
-criu dump --irmap-scan-path /path/to/my/app ...
-```
-These paths are prioritized and scanned before the default hints.
-
-### 3. Caching and Pre-dump
-Scanning large filesystems can be slow and resource-intensive. To mitigate this:
-- **irmap-cache.img**: Scan results are stored in this image file within the images directory.
-- **Pre-dump Optimization**: CRIU can perform the irmap scan during a `pre-dump` while the application is still running. This populates the cache early, significantly reducing the time the application must remain frozen during the final dump.
-- **Validation**: On subsequent runs, CRIU loads the cache and re-validates entries individually (checking if the inode/device still matches the path) rather than performing a full re-scan.
-
-## Support for Filesystems
-
-* **Standard Filesystems**: Works well on most local filesystems (ext4, xfs, etc.).
-* **Tmpfs**: Paths are generally available via `/proc` and don't strictly require Irmap, though it can still be used.
-* **OverlayFS**: Irmap has historically had difficulties with OverlayFS due to how inodes are reported across different layers. In modern kernels, **open_by_handle_at** (leveraging file handles exposed in `/proc/$pid/fdinfo`) is the preferred and more reliable alternative to Irmap.
-
-## See also
-* [FSNotify](fsnotify.md)
-* [Re-opening nameless files](how-to-open-a-file-without-open-system-call.md)
diff --git a/Documentation/under-the-hood/kcmp-trees.md b/Documentation/under-the-hood/kcmp-trees.md
deleted file mode 100644
index d239e2a74..000000000
--- a/Documentation/under-the-hood/kcmp-trees.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Shared Object Detection (Kcmp Trees)
-
-CRIU must frequently determine if system resources (such as file descriptions, memory mappings, or namespaces) are shared between different processes. While some objects have unique kernel-provided IDs (like inode numbers for files on disk), many do not. This document explains how CRIU uses the `kcmp()` system call and red-black trees to efficiently detect these shared objects.
-
-## The Challenge
-
-Comparing every resource in every process against every other process would result in $O(N^2)$ complexity, where $N$ is the total number of resources (e.g., 100 tasks with 100 files each = 10,000 files, or 50 million pairs). This is prohibitively slow.
-
-## The Solution: `kcmp()` and Pointer Comparison
-
-The `kcmp()` system call identifies whether two kernel objects are the same. Crucially, its return value is not a simple boolean; it returns the result of an internal kernel pointer comparison:
-* **0**: The objects are identical.
-* **1**: The first object's pointer is "less than" the second.
-* **2**: The first object's pointer is "greater than" the second.
-* **-1**: Error.
-
-This ordering information allows CRIU to use **red-black trees** to sort and search for objects with $O(N \log N)$ complexity.
-
-## Two-Level Red-Black Trees
-
-To further optimize performance and minimize the number of expensive `kcmp()` system calls, CRIU uses a two-level tree structure:
-
-### Level 1: Fast ID (genid)
-CRIU first calculates a "generation ID" (`genid`) using cheap, locally available metadata. For regular files, this is derived from the device ID, inode number, and current file position.
-* Objects are inserted into a primary red-black tree ordered by `genid`.
-* If two objects have different `genid`s, they are guaranteed to be different, and no system call is needed.
-
-### Level 2: Sub-tree (kcmp)
-If two objects have identical `genid`s, they *might* be the same.
-* CRIU then descends into a sub-tree associated with that `genid`.
-* In this sub-tree, objects are ordered using the `kcmp()` system call.
-* If `kcmp()` returns 0, the objects are confirmed as shared.
-
-## Supported Object Types
-
-CRIU uses `kcmp()` for various object types, including:
-* **KCMP_FILE**: Individual file descriptions.
-* **KCMP_VM**: Virtual memory address spaces.
-* **KCMP_FILES**: The entire file descriptor table.
-* **KCMP_FS**: Filesystem information (umask, root, cwd).
-* **KCMP_SIGHAND**: Signal handler tables.
-* **KCMP_IO**: I/O context.
-* **KCMP_SYSV_SEM**: System V semaphore undo lists.
-* **KCMP_EPOLL_TFD**: Specific descriptors within an epoll interest list.
-
-## See also
-* [Dumping File Descriptors](dumping-files.md)
-* [Copy-on-write memory](copy-on-write-memory.md)
diff --git a/Documentation/under-the-hood/kerndat.md b/Documentation/under-the-hood/kerndat.md
deleted file mode 100644
index eb56194e1..000000000
--- a/Documentation/under-the-hood/kerndat.md
+++ /dev/null
@@ -1,36 +0,0 @@
-# Kerndat (Kernel Data)
-
-**Kerndat** is a CRIU module responsible for detecting the capabilities and features of the currently running Linux kernel. Since CRIU's functionality depends heavily on specific kernel system calls and behaviors, runtime detection is essential for ensuring compatibility and selecting the most efficient algorithms.
-
-## Feature Detection
-
-CRIU performs a wide array of checks during initialization. These include:
-* **System Call Availability**: Checking for `kcmp()`, `userfaultfd()`, `memfd_create()`, `clone3()`, `openat2()`, `membarrier()`, and more.
-* **Filesystem Features**: Verifying `pagemap` functionality, `PAGEMAP_SCAN` support, and anonymous shared mapping behaviors.
-* **Namespace Support**: Detecting Time namespaces, CGroup namespaces, and namespace-specific identifiers.
-* **Architecture-Specific Quirks**: Identifying known CPU bugs or features, such as the x86 FPU/XSAVE ptrace bug.
-
-The results of these checks are stored in a global `kdat` structure, which other CRIU modules query to determine how to proceed during dump and restore operations.
-
-## Persistent Caching
-
-Executing hundreds of kernel feature checks can be time-consuming. To speed up subsequent CRIU invocations, the results are cached on disk.
-
-* **Cache Location**:
- * **Root**: `/run/criu.kdat` (typically stored on `tmpfs` to ensure it is cleared on reboot).
- * **Non-root**: `$XDG_RUNTIME_DIR/criu.kdat`.
-* **Lifecycle**: CRIU attempts to load this cache during `kerndat_init()`. If the cache is missing or stale (e.g., if the CRIU binary has been updated with new checks), CRIU performs a full detection and saves the new results back to the cache file.
-
-## Kerndat vs. Inventory
-
-It is important to distinguish between **kerndat** and the **inventory image** (`inventory.img`):
-* **Kerndat**: Captures the capabilities of the **host kernel**. It is system-wide and typically survives across different CRIU operations on the same host.
-* **Inventory**: Captures critical metadata about a **specific checkpoint**. It is stored within the images directory and includes the CRIU version used for the dump, the host's LSM type (SELinux/AppArmor), and the root task's original IDs.
-
-## Inspection
-
-To see the features detected by CRIU on your current system, use the check command:
-```bash
-criu check --extra
-```
-This command triggers a kerndat initialization and prints the status of various required and optional kernel features, allowing you to verify that your environment is ready for CRIU.
diff --git a/Documentation/under-the-hood/mac-vlan.md b/Documentation/under-the-hood/mac-vlan.md
deleted file mode 100644
index 8bd59ea22..000000000
--- a/Documentation/under-the-hood/mac-vlan.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Mac-VLAN
-
-Mac-VLAN is a Linux network driver that allows creating multiple virtual interfaces with unique MAC addresses on top of a single physical interface. These virtual interfaces act as standalone devices on the network, each with its own IP and MAC address.
-
-## Checkpoint and Restore of Mac-VLAN
-
-CRIU identifies Mac-VLAN interfaces by monitoring netlink messages (specifically `RTM_NEWLINK`) and inspecting their attributes.
-
-### 1. Checkpointing
-During a dump, CRIU extracts the following attributes for each Mac-VLAN device:
-- **Parent Interface**: The physical device (or "upper" link) that the Mac-VLAN is built upon (identified via `IFLA_LINK`).
-- **Mode**: The specific Mac-VLAN operational mode (e.g., `bridge`, `private`, `vepa`, `passthru`), extracted from `IFLA_MACVLAN_MODE`.
-- **Flags**: Any additional configuration flags associated with the interface (`IFLA_MACVLAN_FLAGS`).
-- **MAC Address**: The unique hardware address of the virtual interface.
-
-### 2. Restoration
-To recreate a Mac-VLAN interface exactly as it was, CRIU performs the following:
-- **Link Creation**: It sends an `RTM_NEWLINK` netlink message with the kind set to `"macvlan"`, specifying the original mode and the link to the parent device.
-- **Index Preservation**: To ensure that any application sockets bound to the interface index remain valid, CRIU uses the `IFLA_NEW_IFINDEX` attribute. This allows CRIU to request the exact same interface index that the device possessed before the checkpoint. (This kernel feature was originally developed specifically to support CRIU).
-- **Namespace Migration**: Once created, the interface is moved into the target network namespace for the restored process.
-
-## External Interface Mapping
-
-Since the parent physical interface may have a different name or index on the destination host during migration, CRIU provides the `--external` option to map these dependencies:
-
-```bash
-# Example mapping of an internal macvlan interface to a host physical interface
-criu restore --external macvlan[eth0]:phys0 ...
-```
-
-This tells CRIU that the Mac-VLAN interface which was originally attached to `eth0` should now be attached to the physical interface `phys0` on the current host.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Networking](networking.md)
diff --git a/Documentation/under-the-hood/memory-changes-tracking.md b/Documentation/under-the-hood/memory-changes-tracking.md
deleted file mode 100644
index d113245eb..000000000
--- a/Documentation/under-the-hood/memory-changes-tracking.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Memory Changes Tracking
-
-Memory changes tracking (also known as "dirty memory tracking") is a critical feature in CRIU that enables efficient **live migration** with minimal downtime. By identifying and capturing only the memory pages that have been modified since a previous snapshot, CRIU can perform iterative dumps while the application continues to run.
-
-## The Problem: Memory Dump Latency
-
-During a standard checkpoint, CRIU freezes the process tree and dumps its entire memory state to disk. For memory-intensive applications (like large databases), this process can take several seconds, during which the application is completely unresponsive. This "freeze time" is directly proportional to the amount of memory used by the application.
-
-## The Solution: Iterative Dumps
-
-To minimize freeze time, CRIU supports an iterative migration scheme:
-1. **Initial Pre-dump**: Capture a full snapshot of the application's memory while it is still running.
-2. **Subsequent Pre-dumps**: Periodically capture only those pages that have been modified (made "dirty") since the last pre-dump.
-3. **Final Dump**: Freeze the processes and capture the final set of dirty pages. Since most memory was already transferred in previous steps, the final freeze time is significantly reduced.
-
-## Kernel Mechanisms for Tracking
-
-CRIU relies on two primary kernel mechanisms to track dirty pages:
-
-### 1. The Soft-Dirty Bit
-Linux maintains a "soft-dirty" bit for each Page Table Entry (PTE).
-* **Resetting**: CRIU enables tracking by writing "4" to `/proc/$pid/clear_refs`, which clears the soft-dirty bit for all pages in the task's address space.
-* **Tracking**: Any subsequent write to a page causes the kernel to set its soft-dirty bit.
-* **Reading**: CRIU identifies dirty pages by reading the bit from the process's `/proc/$pid/pagemap` interface.
-
-### 2. ioctl(PAGEMAP_SCAN)
-Reading the entire `/proc/$pid/pagemap` file can be slow for very large address spaces. Modern kernels (v6.7+) support the `PAGEMAP_SCAN` ioctl, which allows CRIU to:
-* **Efficient Scanning**: Identify dirty pages across a large address space in a single kernel call.
-* **Filtering**: Directly filter for specific page categories (e.g., only dirty and present pages).
-* **Atomic Reset**: Optionally clear the soft-dirty bit while scanning, ensuring no writes are missed between scanning and resetting.
-
-CRIU automatically detects and uses `PAGEMAP_SCAN` if available, falling back to manual `/proc` parsing on older kernels.
-
-## Implementation in CRIU
-
-Iterative migration is managed through the `pre-dump` command:
-
-1. **Chained Images**: Each pre-dump creates a set of image files in a new directory. These directories are linked together using the `--prev-images-dir` option.
-2. **Consolidated Restore**: During restoration, CRIU traverses the chain of images from newest to oldest. For any given memory address, it restores the most recent version of the page found in the image stack.
-3. **Page Server**: To avoid writing iterative dumps to disk, they can be sent over the network to a **page server** on the destination host.
-
-## See also
-* [Iterative Migration](iterative-migration.md)
-* [Memory Images Deduplication](memory-images-deduplication.md)
-* [Page Server](page-server.md)
diff --git a/Documentation/under-the-hood/memory-dumping-and-restoring.md b/Documentation/under-the-hood/memory-dumping-and-restoring.md
deleted file mode 100644
index cc0112596..000000000
--- a/Documentation/under-the-hood/memory-dumping-and-restoring.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Memory Dumping and Restoring
-
-Dumping and restoring the memory of a process tree is one of the most critical and complex tasks performed by CRIU. This document details the mechanisms, optimizations, and kernel interfaces involved in this process.
-
-## The Virtual Memory Layout (VMAs)
-
-A process's address space is composed of several Virtual Memory Areas (VMAs). CRIU identifies these areas by parsing `/proc/$pid/smaps` and `/proc/$pid/map_files/`.
-* **Metadata**: Each VMA's start address, end address, protection flags (read, write, execute), and sharing status (private or shared) are recorded in the `mm-$id.img` file.
-* **Backing Store**: CRIU also records whether a VMA is anonymous (backed by RAM/swap) or file-backed.
-
-## The Dumping Process
-
-Capturing memory contents while maintaining consistency and performance requires a multi-stage approach.
-
-### 1. Parasite Injection
-CRIU cannot efficiently read a process's private memory from the outside. Instead, it injects **parasite code** into the target task. This code runs within the task's own address space and context, allowing it direct access to all memory regions.
-
-### 2. Zero-Copy Dumping (vmsplice)
-To transfer memory from the parasite to the CRIU dumper with minimal overhead, CRIU uses a zero-copy mechanism:
-1. **Pipe Setup**: CRIU creates a pipe and sends one end to the parasite via a Unix domain socket.
-2. **vmsplice**: The parasite uses the `vmsplice()` system call with the `SPLICE_F_GIFT` flag. This effectively "gifts" the memory pages to the kernel's pipe buffer without copying the data in userspace.
-3. **Splice to Image**: The CRIU dumper then uses `splice()` to move the data from the pipe directly into the image file (`pages-$id.img`) or to a network socket (for the page server).
-
-### 3. Page Deduplication and Skipping
-CRIU avoids dumping unnecessary data to save time and space:
-* **Unchanged File Pages**: Read-only, file-backed pages (like library code) that have not been modified are not dumped. CRIU simply records the file and offset to re-map them during restoration.
-* **Dirty Tracking**: Using the **soft-dirty bit** (or `PAGEMAP_SCAN`), CRIU can identify and dump only those pages that have changed since a previous pre-dump.
-
----
-
-## The Restoration Process
-
-Restoring memory involves reconstructing the exact address space layout the application had at the moment of the checkpoint.
-
-### 1. Re-mapping VMAs
-During the early stages of restoration, each process calls `mmap()` to recreate its VMAs based on the data in `mm-$id.img`.
-* **Anonymous Memory**: Mapped as private and anonymous.
-* **File Mappings**: Re-mapped from their original files on disk.
-
-### 2. Filling Memory Contents
-CRIU then repopulates the mappings with the data stored in the `pages-$id.img` files. For efficiency, CRIU uses its own optimized I/O routines to read the images and fill the memory regions.
-
-### 3. COW Preservation
-CRIU uses a specialized strategy to ensure that memory shared via `fork()` (Copy-on-Write) remains shared after restoration. This minimizes the total physical memory footprint of the restored process tree. See [COW Memory](copy-on-write-memory.md) for details.
-
-## Advanced Migration Techniques
-
-* **Page Server**: During live migration, memory pages are sent over the network to a page server on the destination host, avoiding expensive disk I/O.
-* **Lazy Migration (Userfaultfd)**: CRIU can restore a process immediately without its memory and then load pages on demand as the application accesses them. This is powered by the `userfaultfd` kernel feature and is essential for reducing initial downtime.
-
-## See also
-* [Memory Changes Tracking](memory-changes-tracking.md)
-* [Copy-on-write Memory](copy-on-write-memory.md)
-* [Userfaultfd](userfaultfd.md)
-* [Page Server](page-server.md)
diff --git a/Documentation/under-the-hood/memory-images-deduplication.md b/Documentation/under-the-hood/memory-images-deduplication.md
deleted file mode 100644
index 6186ed646..000000000
--- a/Documentation/under-the-hood/memory-images-deduplication.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Memory Images Deduplication
-
-During iterative migration, CRIU produces multiple snapshots of a process's memory. Since most memory pages remain unchanged between iterations, saving every page in every snapshot would result in significant disk space waste and increased migration time. CRIU uses several deduplication techniques to address this.
-
-## How Deduplication Works
-
-Deduplication relies on identifying pages that are identical to those in a previous snapshot (the "parent" image).
-
-### 1. The `in_parent` Flag
-The `pagemap-$id.img` file describes the virtual memory layout. Each entry (`pagemap_entry`) can include an `in_parent` flag:
-* **If `false`**: The page's contents are stored in the current `pages-$id.img` file.
-* **If `true`**: The page's contents are identical to the one in the parent image. CRIU does not write the data to the current `pages-$id.img`, saving both disk space and I/O time.
-
-### 2. Detection via Soft-Dirty
-During a `pre-dump`, CRIU uses the kernel's **soft-dirty bit** to identify which pages have been modified.
-* If a page was present in the previous iteration and its soft-dirty bit is **not set**, CRIU knows the content remains unchanged.
-* It marks the page as `in_parent` in the current pagemap image and skips dumping its data.
-
-## Auto-Deduplication (`--auto-dedup`)
-
-CRIU provides an advanced `--auto-dedup` mode that optimizes both the dumping and restoration processes.
-
-### During Dump
-When `--auto-dedup` is enabled during a dump, CRIU actively manages the relationship between the current and parent image sets to ensure maximum deduplication efficiency. It traverses the previous images to verify which regions can be safely referenced rather than re-dumped.
-
-### During Restore (Disk Space Optimization)
-A unique and powerful feature of `--auto-dedup` during restoration is **online disk space reclamation**:
-* As CRIU reads pages from the `pages-$id.img` files to restore the process's memory, it uses the `fallocate(FALLOC_FL_PUNCH_HOLE)` system call on the image files.
-* This "punches holes" in the images, effectively freeing the underlying physical disk blocks as soon as the data has been loaded into RAM.
-* This is critical for systems with limited disk space when restoring from a large number of iterative pre-dumps, as it prevents the total image size from exceeding the available storage.
-
-## Implementation Details
-
-* **Image Chaining**: Deduplication requires a chain of images established via the `--prev-images-dir` option, allowing CRIU to look back through multiple layers of snapshots.
-* **Sparse File Support**: The hole-punching mechanism leverages the host filesystem's support for sparse files, ensuring that the restored environment remains efficient.
-
-## See also
-* [Memory Changes Tracking](memory-changes-tracking.md)
-* [Iterative Migration](iterative-migration.md)
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md)
diff --git a/Documentation/under-the-hood/mount-points.md b/Documentation/under-the-hood/mount-points.md
deleted file mode 100644
index 0f8c998e7..000000000
--- a/Documentation/under-the-hood/mount-points.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Checkpoint and Restore of Mount Points
-
-CRIU provides deep support for capturing and reconstructing Linux mount namespaces and the complex hierarchies of mount points within them. This includes support for bind mounts, shared propagation, and external dependencies.
-
-## Key Information Captured
-
-For every mount namespace, CRIU parses `/proc/$pid/mountinfo` to extract:
-1. **Mount Hierarchy**: The parent-child relationships between mount points.
-2. **Filesystem Details**: Device IDs, filesystem types, and the mount source.
-3. **Root and Target**: The specific directory within the filesystem being mounted and its destination in the process's view.
-4. **Propagation State**: Whether a mount is `shared`, `slave`, `private`, or `unbindable`.
-5. **Mount Options**: Flags such as `ro`, `nodev`, `noexec`, and `nosuid`.
-
-## The Restoration Challenge
-
-Restoring mounts is one of CRIU's most difficult tasks because it must recreate the exact same state that the kernel built up over time. This requires:
-* **Dependency Sorting**: Mounts must be recreated in the correct order (e.g., a parent must exist before its child can be mounted).
-* **Source Resolution**: CRIU must be able to access the original filesystem source.
-* **Propagation Reconstruction**: Shared and slave relationships must be established in the correct sequence to ensure future mount events propagate as expected.
-
-## Mount V2: The Modern Engine
-
-CRIU includes an advanced restoration engine called **Mount V2** (`--mount-v2`). This engine uses a more robust algorithm to handle:
-* **Complex Overmounts**: Scenarios where multiple mounts are stacked on the same directory.
-* **Circular Dependencies**: Resolving cases where mounts depend on each other in non-trivial ways.
-* **Namespace Sharing**: Efficiently handling processes that share the same mount namespace.
-
-## External and Auto-detected Mounts
-
-Sometimes, the source of a mount point is located outside the container or process tree being checkpointed (e.g., a host directory bind-mounted into a container).
-
-### 1. External Mounts (`--external`)
-Users can manually specify how to handle these external dependencies by mapping the mount's identifier to a path on the destination host:
-```bash
-criu restore --external mnt[ID]:/new/host/path ...
-```
-
-### 2. Auto-detection
-CRIU can often automatically identify external bind mounts by comparing the mount points in the target process with those in its own mount namespace. This simplifies migration by reducing the need for manual mapping.
-
-## Common Issues
-
-* **Unsupported Filesystems**: Some specialized or virtual filesystems may not support standard checkpointing. These often require plugins or must be marked as external.
-* **Hidden Sources**: If a bind mount's source is overmounted and no longer visible, CRIU may fail to identify how to recreate it without the Mount V2 engine or manual hints.
-
-## See also
-* [Mount V2 Details](mount-v2.md)
-* [Filesystem Peculiarities](filesystems-pecularities.md)
-* [Invisible Files (Overmounts)](invisible-files.md)
diff --git a/Documentation/under-the-hood/mount-points20.md b/Documentation/under-the-hood/mount-points20.md
deleted file mode 100644
index f92943a7b..000000000
--- a/Documentation/under-the-hood/mount-points20.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Mount Points 2.0 (Legacy)
-
-> **Note**: This document describes an early design iteration for mount restoration. The current and much more advanced implementation is documented in [Mount V2](mount-v2.md).
-
-For detailed information on the modern mount restoration algorithm, including the use of detached mounts and `move_mount`, please refer to:
-* [Mount V2 Overview](mount-v2.md)
-* [Mount V2 Detailed Algorithm](mounts-v2-virtuozzo.md)
-* [Mount Points (General)](mount-points.md)
diff --git a/Documentation/under-the-hood/mount-v2.md b/Documentation/under-the-hood/mount-v2.md
deleted file mode 100644
index 40eaa9916..000000000
--- a/Documentation/under-the-hood/mount-v2.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Mount V2: Advanced Mount Restoration
-
-Introduced in CRIU v3.16, **Mount V2** is a sophisticated restoration engine that leverages modern Linux kernel APIs to handle complex mount hierarchies, propagation groups, and overmounts with high reliability.
-
-## Why Mount V2 was Necessary
-
-The original mount restoration mechanism (Mount V1) relied on sequential, path-based `mount()` calls. This approach had several critical flaws:
-1. **Overmount Sensitivity**: If a directory was already covered by another mount, performing a path-based mount on it could fail or target the wrong filesystem.
-2. **Circular Dependencies**: Resolving mounts that depend on each other in non-linear ways was difficult and often resulted in ordering failures.
-3. **Propagation Complexity**: Establishing `shared` and `slave` relationships required creating dummy mount points and performing specific sequences of `mount --make-shared/slave` calls, which was fragile in complex scenarios.
-
-## How Mount V2 Works
-
-Mount V2 moves away from path-based mounting, instead using **File Descriptor-based** mounting provided by newer kernel system calls.
-
-### 1. Detached Mounts
-CRIU creates each required mount as a **detached mount**. These mounts exist in the kernel but are not yet attached to any visible path in the filesystem.
-* **New Filesystems**: Created using `fsopen()` and `fsmount()`.
-* **Bind Mounts**: Created using `open_tree()` with the `OPEN_TREE_CLONE` flag to create an unattached clone of an existing path.
-
-### 2. Precise Propagation Grouping
-Using the `move_mount()` syscall with the `MOVE_MOUNT_SET_GROUP` flag (introduced in kernel v5.15), CRIU can explicitly assign a detached mount to a specific **shared or slave propagation group**. This eliminates the need for dummy mounts and ensures that the propagation state is perfectly restored as recorded in the images.
-
-### 3. Tree Construction via File Descriptors
-CRIU constructs the entire mount hierarchy by attaching child mounts to their parents using their respective file descriptors. Since this happens "off-line" (outside of any mount namespace), it is immune to path shadowing, path resolution errors, or overmounting issues.
-
-### 4. Atomic Final Attachment
-Once the complete hierarchy is assembled as a tree of detached mounts, CRIU performs a final `move_mount()` to attach the root of this reconstructed tree into the target mount namespace at the desired destination path.
-
-## Kernel Requirements
-
-Mount V2 requires a modern kernel that supports:
-* `fsopen()`, `fsmount()`, `move_mount()` (Kernel v5.2+)
-* `open_tree()` (Kernel v5.3+)
-* `MOVE_MOUNT_SET_GROUP` (Kernel v5.15+)
-
-CRIU automatically detects these features during the [Kerndat](kerndat.md) phase. It will fall back to the older Mount V1 engine if these calls are unavailable, though many modern container layouts now effectively require Mount V2 for a successful restore.
-
-## See also
-* [Mount Points](mount-points.md)
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Kerndat](kerndat.md)
diff --git a/Documentation/under-the-hood/mounts-v2-virtuozzo.md b/Documentation/under-the-hood/mounts-v2-virtuozzo.md
deleted file mode 100644
index 4219babb6..000000000
--- a/Documentation/under-the-hood/mounts-v2-virtuozzo.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Mount V2: Detailed Algorithm
-
-The Mount V2 engine (originally developed by Virtuozzo and later merged into upstream CRIU) is designed to resolve complex issues with restoring sharing groups, over-mounted files, and cross-namespace bind mounts. This document provides a technical breakdown of its operation.
-
-## 1. Mount Image Processing Stage
-
-During initialization, CRIU processes the mount images for all namespaces to build an internal model of the filesystem state:
-- **Hierarchy Construction**: Build a per-namespace mount tree based on parent IDs.
-- **Bind Grouping**: Group mounts by superblock equality into "bind" lists to identify shared underlying filesystems.
-- **Sharing Groups**: Organize shared and slave groups into a tree structure (e.g., where a parent's `shared_id` matches a child's `master_id`).
-- **The Root Yard**: Create a helper mount (`root_yard_mp`) at a temporary location (e.g., `/tmp/.criu.mntns.XXXXXX/`). All mount trees from all namespaces are initially merged as subdirectories of this "root yard."
-
-## 2. Pre-Fork Mounting Stage
-
-This stage is executed from the init task in a dedicated "service" mount namespace before the target process tree is forked:
-1. **Plain Mounting**: CRIU walks the merged mount tree and creates all mounts in a "plain" (unattached) and "private" state.
-2. **Source Resolution**: For each mount, CRIU identifies its source (a real filesystem, a bind mount from another already-mounted superblock, or an external source).
-3. **Cross-Namespace Handling**: By maintaining all mounts within a single service namespace during this stage, CRIU can easily handle bind mounts that cross namespace boundaries.
-
-## 3. Propagation and Shared Group Restoration
-
-CRIU restores complex propagation relationships using modern kernel APIs:
-- **Slavery and Sharing**: For each sharing group, CRIU identifies the "master" mount. It uses the `move_mount()` system call with the `MOVE_MOUNT_SET_GROUP` flag (or the legacy `MS_SET_GROUP` mechanism) to establish slave/shared relationships precisely as they existed during the dump.
-- **Settings Replication**: Once the sharing state is established for the primary mount in a group, all other members of the group inherit these settings.
-
-## 4. Namespace Transition and Final Positioning
-
-For each target mount namespace being restored:
-1. **Unshare**: CRIU calls `unshare(CLONE_NEWNS)` to create a fresh, empty mount namespace.
-2. **Tree Positioning**: Move the "plain" mounts from the root yard into their final hierarchical positions within the new namespace using `move_mount()`.
-3. **Pivot Root**: Execute `pivot_root()` to switch to the new namespace root, effectively hiding the temporary "yard" and finalizing the mount hierarchy.
-
-## 5. Post-Fork Fixups
-
-Certain mounts cannot be fully restored until the process tree is established:
-- **Delayed Procfs**: `proc` mounts for nested PID namespaces must wait until the target PID namespace is created. CRIU enters these namespaces after forking to perform the final mounts.
-- **Internal Yards**: In some cases, temporary `tmpfs` mounts ("internal yards") are used within a namespace to hold mounts that must be moved or adjusted after the process tree is fully alive.
-
-## See also
-* [Mount V2 Overview](mount-v2.md)
-* [Mount Points](mount-points.md)
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
diff --git a/Documentation/under-the-hood/nfs-mount-points.md b/Documentation/under-the-hood/nfs-mount-points.md
deleted file mode 100644
index 347cad9f8..000000000
--- a/Documentation/under-the-hood/nfs-mount-points.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# NFS mount points
-
-When C/R-ing NFS mount points there a chicken-and-egg problem.
-
-
diff --git a/Documentation/under-the-hood/optimizing-pre-dump-algorithm.md b/Documentation/under-the-hood/optimizing-pre-dump-algorithm.md
deleted file mode 100644
index c822d178c..000000000
--- a/Documentation/under-the-hood/optimizing-pre-dump-algorithm.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Optimized Pre-dump Algorithm
-
-Pre-dumping is the process of capturing dirty memory pages while an application continues to run, aiming to minimize the final "freeze time" during live migration. CRIU provides two primary modes for pre-dumping: `read` and `splice`.
-
-## Traditional vs. Optimized Pre-dump
-
-### The `read` Mode (Traditional)
-In this mode, CRIU uses the `process_vm_readv` system call to read memory from the target process.
-* **Workflow**: Tasks are briefly frozen to identify dirty pages and reset the soft-dirty bit, then resumed. CRIU then reads the pages from the running process's address space.
-* **Challenge**: Reading memory while a process is running can lead to minor inconsistencies if the process modifies a page *while* it is being read (see [Memory Consistency](#memory-consistency) below). Furthermore, `process_vm_readv` requires the target process to be alive and its memory mappings to remain stable during the read.
-
-### The `splice` Mode (Optimized & Default)
-The `splice` mode (enabled via `--pre-dump-mode=splice`) uses a zero-copy "gift" mechanism to further reduce freeze time and improve reliability.
-
-#### How `splice` Mode Works:
-1. **Brief Freeze**: CRIU seizes the tasks and injects the parasite code.
-2. **vmsplice "Gifting"**: The parasite identifies dirty pages and calls `vmsplice()` with the `SPLICE_F_GIFT` flag. This flag tells the kernel that the process is "giving" these pages to a pipe.
-3. **Immediate Resume**: Once the `vmsplice()` calls are complete (which is extremely fast as no data is actually copied), the parasite is removed, and the tasks are resumed immediately.
-4. **Parallel Draining**: While the tasks are running, the main CRIU process "drains" the data from the pipes and writes it to the image files or sends it to the page server.
-
-#### Why `splice` is Better:
-* **Minimized Downtime**: The "freeze" duration is reduced to just the time needed for the parasite to execute the `vmsplice()` system calls, rather than the time needed to transfer memory data over the network or to disk. This scheme relies entirely on `vmsplice()` being extremely fast. Because the target process is frozen during these calls, minimizing this duration is critical to the primary goal of pre-dumping: reducing process downtime during live migration and making the migration process almost invisible to the application.
-* **Zero-Copy Transfer**: By gifting pages directly from the target process to the pipe, `splice` mode avoids copying memory to CRIU user-space buffers (unlike `read` mode which uses `process_vm_readv` to copy data). While this zero-copy mechanism does not use COW (meaning intermediate dumps can be inconsistent if pages are modified after resume), CRIU's iterative design handles this inconsistency (see below) while maximizing transfer performance.
-
-## Memory Consistency
-
-Because the target process is resumed while CRIU is still writing the memory data (in both `read` and `splice` modes), intermediate pre-dump images may contain inconsistent memory states.
-
-This inconsistency is expected and handled by CRIU's iterative design:
-1. **Tracking Changes**: CRIU uses the kernel's soft-dirty tracker to monitor memory writes after the process is resumed.
-2. **Subsequent Dumps**: Any page modified after it has been pre-dumped is marked dirty again and will be captured in the next pre-dump iteration or during the final dump.
-3. **Restoring Consistency**: During restore, CRIU applies the dump images in sequence (from oldest pre-dump to the final dump). The final dump is taken while the process is fully frozen, ensuring that the final state of all memory pages is consistent.
-
-## Usage
-
-The optimized `splice` mode is the default in modern CRIU. It can be explicitly requested using the `--pre-dump-mode` option:
-```bash
-criu pre-dump --pre-dump-mode splice ...
-```
-
-## See also
-* [Memory Changes Tracking](memory-changes-tracking.md)
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md)
-* [Iterative Migration](iterative-migration.md)
diff --git a/Documentation/under-the-hood/pagemap-cache.md b/Documentation/under-the-hood/pagemap-cache.md
deleted file mode 100644
index 4ad7a4e5d..000000000
--- a/Documentation/under-the-hood/pagemap-cache.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Pagemap Cache
-
-When dumping the memory of a process, CRIU must frequently query the kernel to determine which virtual memory pages are currently present in RAM, swapped out, or modified (dirty). This information is typically retrieved from the `/proc/$pid/pagemap` file. However, reading and parsing this file repeatedly for every Virtual Memory Area (VMA) is inefficient. To solve this, CRIU implements a high-performance **Pagemap Cache**.
-
-## The Performance Problem
-
-The `/proc/$pid/pagemap` file is a 64-bit-per-page binary stream. For a process with a large address space, this file can be several megabytes in size. While a single sequential read is fast, CRIU needs this data across multiple stages of the dump (e.g., initial size estimation, private memory dumping, shared memory dumping, and iterative pre-dumps). Performing multiple full reads and frequent `lseek()` calls into this file introduces significant overhead, especially for applications with thousands of small VMAs.
-
-## Implementation Details
-
-The pagemap cache (`struct pmc`) optimizes access through several advanced techniques:
-
-### 1. Sliding Window Caching
-Instead of reading the entire pagemap at once, CRIU uses a sliding window (typically **2MB** in size, defined as `PMC_SIZE`).
-* When a VMA is accessed that is not currently in the cache (a cache miss), the cache "refills" itself by reading the pagemap for the required range.
-* **Greedy Prefetching**: If the current VMA is small, the cache tries to fill the remainder of the 2MB window by pre-reading information for subsequent, adjacent VMAs. This significantly reduces the total number of `read()` system calls and minimizes the overhead of kernel-side page table walks.
-
-### 2. ioctl(PAGEMAP_SCAN) Integration
-On modern kernels (v6.7+), the pagemap cache leverages the `PAGEMAP_SCAN` ioctl. This interface is far more efficient than the legacy `/proc` file:
-* **Bulk Retrieval**: It allows CRIU to fetch information for multiple, non-contiguous page ranges in a single kernel call.
-* **Kernel-Side Filtering**: CRIU can instruct the kernel to only return information for specific categories of pages (e.g., pages that are both present in RAM and marked as "soft-dirty"), further reducing the amount of data transferred and processed in userspace.
-
-### 3. Cache Invalidation
-To ensure consistency, the pagemap cache is per-process and is strictly managed:
-* The cache is typically populated while the target process is **frozen** to ensure a stable view of memory.
-* The cache is invalidated whenever the process state might have changed or when the dumper transitions between different memory processing phases.
-
-## Debugging and Control
-
-The pagemap cache can be disabled for troubleshooting or performance comparison by setting the `CRIU_PMC_OFF` environment variable.
-
-## See also
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md)
-* [Memory Changes Tracking](memory-changes-tracking.md)
-* [Optimizing Pre-dump Algorithm](optimizing-pre-dump-algorithm.md)
diff --git a/Documentation/under-the-hood/parasite-code.md b/Documentation/under-the-hood/parasite-code.md
deleted file mode 100644
index 6dcd3f413..000000000
--- a/Documentation/under-the-hood/parasite-code.md
+++ /dev/null
@@ -1,54 +0,0 @@
-# Parasite Code Injection and Execution
-
-The **parasite code** is a specialized binary blob that CRIU injects into the address space of a target process during a checkpoint. Its primary purpose is to extract internal task state—such as private memory contents, credentials, and signal handlers—that is not available via standard kernel interfaces like `/proc`.
-
-## The Infection Process
-
-Infection is a multi-stage operation managed by the **Compel** sub-project, leveraging the `ptrace` system call to take control of the target process.
-
-### 1. Seizing the Task
-CRIU stops the target task using `PTRACE_SEIZE` followed by `PTRACE_INTERRUPT`. This ensures a non-disruptive stop without delivering signals to the application, maintaining transparency.
-
-### 2. Bootstrap Payload
-CRIU identifies the task's current instruction pointer (`RIP`/`PC`) and uses `PTRACE_POKEDATA` to temporarily inject a small bootstrap payload. This payload is typically designed to execute a system call (such as `mmap` or `memfd_create`) to allocate a dedicated memory region for the full parasite blob.
-
-### 3. Memory Exchange Optimization
-To maximize efficiency and avoid thousands of slow `ptrace` calls, CRIU uses a **memory exchange** technique:
-* The parasite's memory region is often backed by a file descriptor (e.g., `memfd`).
-* CRIU maps this same file descriptor into its own address space.
-* This allows the CRIU coordinator to write the parasite code, Global Offset Table (GOT), and arguments directly into the target's memory at local memory speeds.
-
-### 4. Relocation and GOT Patching
-Since the parasite is a Position-Independent Executable (PIE), CRIU must patch its GOT table with the actual addresses where the blob was mapped in the target process's address space.
-
-### 5. Starting the Daemon
-CRIU sets the task's instruction pointer to the entry point of the parasite and resumes execution using `PTRACE_CONT`. The parasite initializes its own stack, sets up signal handling for its own internal use, and enters **daemon mode**.
-
-## Execution and Communication
-
-The parasite runs as a daemon within the target task's context, communicating with the main CRIU process via a Unix domain socket.
-
-### Control Loop
-The parasite enters a loop where it waits for commands from the CRIU coordinator. Each command follows a **Request-Response** pattern:
-1. **Request**: CRIU sends a command ID (e.g., `PARASITE_CMD_DUMP_PAGES`) and any necessary arguments through the socket.
-2. **Execution**: The parasite executes the requested action within the task's context (e.g., calling `vmsplice` on its own memory).
-3. **Response**: The parasite sends an acknowledgment (ACK) and optional data back to CRIU.
-
-### Supported Actions
-* **Memory Dumping**: Efficiently transfers memory pages to CRIU using the `vmsplice()` system call.
-* **Credential Extraction**: Captures UIDs, GIDs, and capability sets.
-* **Timer and Signal State**: Reads interval timers and signal action tables that are not visible through `/proc`.
-* **Thread Coordination**: In multi-threaded processes, the parasite coordinates state collection across all threads.
-
-## Cleanup and Cure
-
-Once the state capture is complete, CRIU performs a "cure" operation to return the process to its original state:
-1. CRIU sends the `PARASITE_CMD_FINI` command to the daemon.
-2. The parasite unmaps its allocated memory and prepares to exit.
-3. CRIU restores the original register state (including the instruction pointer) and the original code bytes that were overwritten during the bootstrap phase.
-4. CRIU detaches from the task, allowing it to resume normal operation or terminating it as requested.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Code Blobs](code-blobs.md)
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md)
diff --git a/Documentation/under-the-hood/pending-signals.md b/Documentation/under-the-hood/pending-signals.md
deleted file mode 100644
index 57499940d..000000000
--- a/Documentation/under-the-hood/pending-signals.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Pending Signals
-
-In Linux, a signal is marked as **pending** if it has been delivered to a task but has not yet been handled (e.g., because the signal is blocked or the task is currently stopped). CRIU provides full support for capturing and restoring these pending signal queues, ensuring that the application's signal state remains perfectly consistent across a checkpoint.
-
-## Checkpoint and Restore of Pending Signals
-
-CRIU manages pending signals using specialized `ptrace` interfaces and signal injection system calls.
-
-### 1. Checkpointing (Dumping)
-During a dump, CRIU must extract both the list of pending signals and the detailed metadata associated with each one (the `siginfo_t` structure).
-
-* **PTRACE_PEEKSIGINFO**: CRIU uses this system call (introduced in Linux kernel v3.10 specifically to support CRIU) to read the signal queues of the target task without actually delivering them.
- * **Private Signals**: Signals delivered to a specific thread are read using standard peeking.
- * **Shared Signals**: Signals delivered to the entire process (which can be handled by any thread) are read by adding the `PTRACE_PEEKSIGINFO_SHARED` flag.
-* **Batch Processing**: CRIU reads signals in batches (typically 32 at a time) to efficiently capture entire queues, which is common in high-throughput applications.
-* **Signal Mask**: In addition to the pending signals, CRIU uses `PTRACE_GETSIGMASK` to capture the set of signals currently blocked by each thread. This mask is essential because it determines why the signals were pending in the first place.
-
-### 2. Restoration
-To recreate the pending state, CRIU re-injects the captured signals into the newly created process tree before it begins normal execution.
-
-* **rt_sigqueueinfo()**: For process-wide (shared) signals, CRIU uses this system call to send a signal to a process with the original `siginfo_t` data.
-* **rt_tgsigqueueinfo()**: For thread-specific (private) signals, CRIU uses this variant to target a specific thread ID (TID) within a process.
-* **Preserving siginfo**: These system calls allow CRIU to pass the exact original `siginfo_t` structure (including the sender's PID, UID, and any signal-specific data), ensuring the restored task sees the identical signal context.
-
-## Shared vs. Private Pending Signals
-
-* **Multi-threaded Handling**: In multi-threaded applications, signals are carefully tracked:
- * **Shared signals** are stored in the process leader's `core.img`.
- * **Private signals** are stored in the `core.img` corresponding to each individual thread.
-* **Restore Order**: Signals are restored while the task is still under CRIU's control, ensuring that they remain pending until the task is finally resumed and its original signal mask is applied.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Parasite Code](parasite-code.md)
diff --git a/Documentation/under-the-hood/pid-restore.md b/Documentation/under-the-hood/pid-restore.md
deleted file mode 100644
index d73c82dd9..000000000
--- a/Documentation/under-the-hood/pid-restore.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# PID Restoration
-
-A critical requirement for successful checkpoint/restore is ensuring that each process and thread is restored with its original **Process ID (PID)** and **Thread ID (TID)**. Applications frequently rely on these IDs for inter-process communication, signal delivery, and as keys for shared resources (such as System V IPC).
-
-## Restoration Mechanisms
-
-CRIU employs two primary methods to request specific PIDs from the Linux kernel during restoration.
-
-### 1. The Legacy Interface: `ns_last_pid`
-On older kernels, Linux does not provide a direct way to request a specific PID during a `fork()` or `clone()` call. Instead, CRIU uses the `/proc/sys/kernel/ns_last_pid` interface:
-1. CRIU acquires a global lock (`lock_last_pid`) to minimize the chance of other processes interfering.
-2. It writes `N-1` to `/proc/sys/kernel/ns_last_pid`.
-3. It calls `fork()`.
-4. The kernel assigns the next available PID, which should be `N`.
-
-**Limitations**:
-* **Race Conditions**: Other processes on the system (outside of CRIU's control) might fork and "steal" the intended PID between the write and the fork.
-* **Performance**: Repeatedly writing to the `/proc` filesystem and calling `fork()` is slow, especially for large process trees.
-* **Nesting Complexity**: Handling nested PID namespaces with this interface requires recursively entering namespaces and managing the legacy interface at each level.
-
-### 2. The Modern Interface: `clone3()` with `set_tid`
-Introduced in Linux kernel v5.5, the `clone3()` system call provides a much more robust and efficient mechanism via the `set_tid` array in the `clone_args` structure.
-* **Atomic Assignment**: CRIU explicitly specifies the desired PID directly during the creation call.
-* **No Races**: The PID assignment is atomic with process creation, eliminating the risk of PID theft.
-* **Efficiency**: Offers significant performance improvements, particularly during the restoration of large, multi-threaded applications.
-* **Full Hierarchy Support**: CRIU can pass an array of PIDs to `set_tid`, allowing it to simultaneously set the process's identity in all nested PID namespaces.
-
-## Implementation in CRIU
-
-CRIU includes architecture-specific assembly wrappers (`RUN_CLONE3_RESTORE_FN`) to safely execute these calls during the critical restoration phase.
-
-* **Automatic Selection**: CRIU automatically detects the presence of `clone3()` and `set_tid` support during the [Kerndat](kerndat.md) phase. If the modern interface is available, it is prioritized.
-* **Thread Restoration**: Individual threads are restored using the same mechanisms, ensuring that their TIDs match the original state.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Kerndat](kerndat.md)
-* [Restorer Context](restorer-context.md)
diff --git a/Documentation/under-the-hood/pidfd-store.md b/Documentation/under-the-hood/pidfd-store.md
deleted file mode 100644
index 7e2acd4dd..000000000
--- a/Documentation/under-the-hood/pidfd-store.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Pidfd Store: Reliable Process Identification
-
-The **Pidfd Store** is an internal CRIU mechanism used during iterative migration to reliably identify processes across multiple pre-dump iterations. It leverages the Linux kernel's `pidfd` interface to eliminate the risks associated with PID reuse.
-
-## The Problem: PID Reuse
-
-In an iterative migration workflow, CRIU performs multiple `pre-dump` operations. Each iteration captures memory pages that have changed since the previous snapshot. To do this safely, CRIU must ensure that it is still talking to the *exact same process* it was in the previous iteration.
-
-If a process dies between iterations and the kernel assigns its old PID to a new, unrelated process, a naive check based only on the PID would fail to detect this change. Performing an incremental dump on a new process using the state of an old one would lead to corrupted images and a failed restoration.
-
-## How the Pidfd Store Works
-
-The Pidfd Store allows CRIU to maintain a persistent, race-free handle for every process in the tree.
-
-### 1. Capturing Pidfds
-During the first pre-dump, CRIU calls `pidfd_open()` for every task it captures. Unlike a numeric PID, a **pidfd** is a file descriptor that refers to a specific process *instance*. If that process terminates, its pidfd becomes invalid and will never refer to a subsequent process, even if the numeric PID is reused.
-
-### 2. Persistent Storage via "The Socket Trick"
-CRIU often operates as a service, receiving commands via RPC. To keep pidfds alive between independent RPC calls, CRIU uses a clever "socket trick":
-* CRIU creates a Unix domain socket and connects it to itself.
-* It "sends" the captured pidfds into this socket using the `SCM_RIGHTS` mechanism.
-* The Linux kernel stores these file descriptors in the socket's internal buffer. Because the socket is connected to itself, the descriptors remain queued in the kernel until CRIU explicitly reads them back.
-
-### 3. Identity Verification
-In each subsequent `pre-dump` or the final `dump` command:
-1. CRIU "drains" the pidfds from its internal storage socket.
-2. It builds a hash table mapping PIDs to these stable pidfd handles.
-3. Before capturing state for a PID, CRIU verifies the task against the stored pidfd.
-4. If the pidfd is still valid, CRIU knows it is the same process and can safely perform an incremental memory dump.
-5. If the pidfd is invalid or missing, CRIU detects a **PID reuse** event and treats the process as entirely new, performing a full dump to maintain consistency.
-
-## Kernel Requirements
-
-The Pidfd Store requires modern kernel features (automatically detected via [Kerndat](kerndat.md)):
-* `pidfd_open()` (Kernel v5.3+)
-* `pidfd_getfd()` (Kernel v5.6+, used to transfer the storage socket between service components).
-
-## See also
-* [Memory Changes Tracking](memory-changes-tracking.md)
-* [Iterative Migration](iterative-migration.md)
-* [Kerndat](kerndat.md)
diff --git a/Documentation/under-the-hood/pidfd.md b/Documentation/under-the-hood/pidfd.md
deleted file mode 100644
index f94493ca4..000000000
--- a/Documentation/under-the-hood/pidfd.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# Pidfd Support
-
-A **pidfd** is a file descriptor that refers to a specific process. Unlike traditional numeric PIDs, which can be reused by the kernel once a process terminates, a pidfd is a stable and race-free handle. It remains valid as long as the descriptor is open, even after the process it refers to has died. CRIU provides full support for checkpointing and restoring pidfds owned by applications.
-
-## How CRIU Handles Pidfds
-
-CRIU treats pidfds as a specialized type of file descriptor. During a dump, it captures both the target of the pidfd and its configuration.
-
-### 1. Checkpointing (Dumping)
-When CRIU encounters a pidfd in a process's file descriptor table:
-* **Target Identification**: It parses `/proc/$pid/fdinfo/$fd` to determine the numeric PID that the pidfd currently refers to.
-* **Tree Validation**: CRIU verifies that this target PID is part of the process tree being checkpointed. This ensures that the process will be available for re-binding during restoration.
-* **Metadata Capture**: CRIU records the target process's namespace-local PID and any flags associated with the pidfd (such as `O_NONBLOCK` or `O_CLOEXEC`).
-
-### 2. Restoration
-Restoring a pidfd involves recreating a handle that points to the equivalent process in the newly restored tree.
-
-* **Alive Processes**: If the target process is alive, CRIU simply calls the `pidfd_open()` system call on the restored PID of that task.
-* **Dead Processes**: A unique feature of pidfds is that they can be held open even after the target process has exited. To restore this state, CRIU:
- 1. Creates a temporary "helper" process.
- 2. Opens a pidfd to this helper.
- 3. Terminates the helper process.
- This leaves the restored application with a valid pidfd that refers to a dead process, perfectly mimicking the original state.
-
-## Kernel Evolution: From Anonymous Inodes to `pidfs`
-
-The underlying implementation of pidfds in the Linux kernel has changed over time:
-* **Pre-v6.9**: Pidfds were implemented using anonymous inodes. In `/proc/$pid/fd`, they appeared as `anon_inode:[pidfd]`.
-* **v6.9 and later**: Pidfds are now part of a dedicated **pidfs** filesystem. They appear in `/proc` as `pidfd:[N]`.
-
-CRIU automatically detects these kernel differences and handles both formats transparently, ensuring that pidfds are correctly identified and restored regardless of the host kernel version.
-
-## Current Limitations
-* **PIDFD_THREAD**: Support for pidfds that target specific threads (created with the `PIDFD_THREAD` flag) is currently not implemented.
-
-## See also
-* [Pidfd Store (Iterative Migration)](pidfd-store.md)
-* [PID Restoration](pid-restore.md)
-* [Kerndat Feature Detection](kerndat.md)
diff --git a/Documentation/under-the-hood/restartable-sequences.md b/Documentation/under-the-hood/restartable-sequences.md
deleted file mode 100644
index 50fe3b0b2..000000000
--- a/Documentation/under-the-hood/restartable-sequences.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Restartable Sequences (rseq)
-
-Restartable Sequences (rseq) is a Linux kernel feature (introduced in v4.18) that enables high-performance userspace operations on per-CPU data without requiring atomic instructions or traditional locking. Each thread registers a `struct rseq`, and the kernel ensures that if a thread is preempted or interrupted while inside a critical section, it is "restarted" by jumping to a predefined abort handler.
-
-## The Challenge of C/R with rseq
-
-Checkpointing and restoring rseq is exceptionally delicate because the kernel's rseq state is tightly coupled with process execution.
-
-1. **Dumping Sensitivity**: If an infected thread is allowed to run even briefly (e.g., to execute parasite code), the kernel's `rseq_handle_notify_resume` hook may be triggered. This would cause the kernel to "fix up" the rseq state, clearing critical section pointers in userspace memory and losing the very state CRIU needs to capture.
-2. **Restoration Morphing**: During restoration, CRIU "morphs" into the target process. If the CRIU binary itself was compiled with rseq support (common in modern distributions), it may have an active rseq registration that must be carefully managed before the memory layout is swapped.
-
-## How CRIU Handles rseq
-
-CRIU provides robust support for rseq, ensuring that threads interrupted within a critical section correctly restart after restoration.
-
-### 1. Checkpointing (Dumping)
-CRIU captures the rseq configuration without disturbing the thread's execution state:
-
-* **PTRACE_GET_RSEQ_CONF**: CRIU uses this ptrace command (Kernel v5.13+) to retrieve the address, size, and signature of the `struct rseq` registered for each thread.
-* **External Peeking**: To avoid triggering kernel fixups, CRIU **does not** use its standard parasite code to read rseq-related memory. Instead, it uses `PTRACE_PEEKDATA` to read the `struct rseq` and `struct rseq_cs` (critical section descriptor) directly from the outside while the task is frozen.
-* **Critical Section Detection**: By reading the `rseq_cs` pointer within the `struct rseq`, CRIU identifies if a thread was in the middle of a sequence at the time of the snapshot.
-
-### 2. Restoration
-The restoration process involves two critical rseq-related steps:
-
-* **Unregistering Restorer rseq**: Before CRIU performs the final "morphing" (unmapping its own memory and mapping the application's memory), it must explicitly **unregister** any rseq area used by the CRIU process itself. Failing to do so would cause the kernel to attempt to update a `cpu_id` field in memory that has been unmapped, resulting in an immediate segmentation fault.
-* **Re-establishing Application rseq**: Once the application's memory layout and thread registers are restored, CRIU calls the `rseq()` system call for each thread. It re-registers the original `struct rseq` at its original address.
-* **Automatic Restart**: Because the `rseq_cs` pointer is restored as part of the thread's memory, the kernel will detect the active critical section upon the first resumption and automatically trigger the application's restart/abort logic, ensuring data integrity.
-
-## Kernel Requirements
-
-* **rseq support**: Linux Kernel v4.18+
-* **PTRACE_GET_RSEQ_CONF**: Linux Kernel v5.13+ (Required for reliable automated detection).
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Parasite Code](parasite-code.md)
-* [Restorer Context](restorer-context.md)
diff --git a/Documentation/under-the-hood/restorer-context.md b/Documentation/under-the-hood/restorer-context.md
deleted file mode 100644
index 54de51ef6..000000000
--- a/Documentation/under-the-hood/restorer-context.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Restorer Context
-
-The **Restorer Context** refers to the final stage of the restoration process, where a CRIU process "morphs" itself into the target application. This critical transition is performed by a specialized [PIE](code-blobs.md) blob known as the **Restorer PIE**.
-
-## Why a Dedicated Context is Necessary
-
-During the final stage of restoration, CRIU must accomplish two conflicting goals:
-1. **Memory Swapping**: It must unmap all of CRIU's own code, stack, and data to completely clear the address space for the application.
-2. **Memory Re-mapping**: It must map the application's original memory regions (VMAs) back into their exact original addresses.
-
-While these operations are occurring, some code must remain in the address space to execute the necessary `munmap()` and `mmap()` system calls. The Restorer PIE is designed to reside in a temporary "safe hole" in the address space—a range that does not conflict with either CRIU's temporary mappings or the application's final layout.
-
-## The Restoration Workflow
-
-1. **Preparation**: The root CRIU process identifies the restorer code and prepares it for distribution.
-2. **Forking**: The process tree is recreated. Since the restorer code is mapped in the root task before forking, all child processes share the same physical memory for the restorer via standard Copy-on-Write (COW).
-3. **Safe Hole Detection**: Each restored process scans its target memory layout (from the `mm.img` file) to find a contiguous area large enough to hold the restorer code and its stack.
-4. **Remapping**: Each process uses `mremap()` to move the shared restorer blob to its specific safe hole.
-5. **Execution Jump**: The process jumps from the main CRIU code into the restorer PIE.
-6. **Cleanup and Reconstruction**: The restorer PIE unmaps CRIU, recreates the application's original mappings, and populates them with data from the image files.
-7. **Final Transition (Sigreturn)**: The very last step is calling `sigreturn()`. The restorer prepares a special signal frame on the stack containing the application's original register state (including the instruction pointer). The kernel then loads this state, effectively resuming the application from the exact point of the checkpoint.
-
-## Technical Characteristics
-
-### Freestanding PIE
-Because the restorer runs in an environment where standard libraries have been unmapped, it is a **freestanding** Position-Independent Executable. It contains its own minimal assembly-level system call wrappers and does not depend on `glibc` or any external runtime.
-
-### Conflict Avoidance
-The algorithm for finding the "safe hole" is architecture-specific. It must account for various kernel-mapped regions like the vDSO, the stack, and potential guard pages to ensure that the restorer code never overlaps with memory that the application needs.
-
-## See also
-* [Code Blobs](code-blobs.md)
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md)
diff --git a/Documentation/under-the-hood/service-descriptors.md b/Documentation/under-the-hood/service-descriptors.md
deleted file mode 100644
index a2ddbbdc6..000000000
--- a/Documentation/under-the-hood/service-descriptors.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Service Descriptors (Service FDs)
-
-During dump and restore operations, CRIU requires numerous internal file descriptors (FDs) to manage logs, images, RPC communication, and transport sockets. Because the application being checkpointed or restored may use any arbitrary FD number, CRIU must ensure its internal descriptors never conflict with those of the application. To achieve this, CRIU uses the **Service FD Engine**.
-
-## The Protected Range
-
-CRIU avoids FD collisions by placing its internal descriptors in a "protected" range at the very top of the process's file descriptor table.
-
-* **Lifting Limits**: Upon startup, CRIU attempts to lift its `RLIMIT_NOFILE` resource limit (using `rlimit_unlimit_nofile()`) to a very high value (typically 1,048,576 or higher).
-* **Top-Down Allocation**: Service FDs are allocated starting from the maximum allowed FD number and working downwards. This strategy places them as far as possible from the range typically used by applications (which usually start from 0 and work upwards).
-
-## Service FD Engine Mechanisms
-
-The engine (`criu/servicefd.c`) provides a robust abstraction for managing these descriptors through several key techniques:
-
-### 1. Per-Process Isolation in Shared Tables
-In scenarios where multiple processes share the same file descriptor table (e.g., threads or processes created with `CLONE_FILES`), CRIU assigns a unique `service_fd_id` to each task. The engine uses this ID to offset the service FD range, ensuring that even tasks sharing an FD table have distinct, non-overlapping slots for their internal CRIU descriptors.
-
-### 2. Descriptor Relocation
-When CRIU opens a file for its own use (such as an image file or the log), the kernel initially assigns it the lowest available FD number (e.g., FD 3). CRIU then uses `fcntl(F_DUPFD_CLOEXEC)` or `dup3()` to "move" that descriptor to its designated high-range slot and immediately closes the original low-numbered descriptor.
-
-### 3. Protection Flags and Safety
-During critical phases of restoration—specifically when the application's FDs are being "planted" into their final numeric slots—CRIU sets a global `sfds_protected` flag. While this flag is set, the service FD engine is "locked." Any attempt by the code to modify or close a service descriptor will trigger an immediate safety crash (BUG), preventing accidental corruption of the restoration state.
-
-## Common Service FD Types
-
-The engine manages various types of descriptors, each with a specific role:
-* **LOG_FD**: The descriptor for the main CRIU log file.
-* **IMG_FD**: The descriptor used for accessing image files.
-* **RPC_SK**: The socket used for RPC communication with external management tools.
-* **TRANSPORT_FD**: Sockets used to "send" and "receive" FDs between processes via `SCM_RIGHTS`.
-* **PROC_FD**: A stable handle to the `/proc` filesystem.
-* **CGROUP_YARD**: A descriptor for the temporary directory used during cgroup restoration.
-
-## See also
-* [Dumping File Descriptors](dumping-files.md)
-* [Descriptor Assignment](how-to-assign-needed-file-descriptor-to-a-file.md)
diff --git a/Documentation/under-the-hood/shared-memory.md b/Documentation/under-the-hood/shared-memory.md
deleted file mode 100644
index abe29dcbc..000000000
--- a/Documentation/under-the-hood/shared-memory.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Shared Memory
-
-CRIU provides comprehensive support for capturing and reconstructing the various ways processes share memory in Linux. This includes anonymous shared regions, file-backed shared mappings, and System V IPC segments.
-
-## Types of Shared Memory
-
-CRIU categorizes shared memory into three primary types, each with a dedicated restoration strategy:
-
-### 1. Shared Anonymous Mappings
-Created via `mmap(..., MAP_SHARED | MAP_ANONYMOUS, ...)`, these regions have no persistent backing file on disk but are shared between a parent and its children after a `fork()`, or between unrelated processes that inherit the mapping.
-
-* **Identification**: CRIU identifies these regions by parsing `/proc/$pid/maps`. For shared anonymous regions, the kernel assigns a unique **internal inode number** (often appearing in the `inode` column of maps). CRIU uses this inode number as a `shmid` to group and identify identical mappings across the entire process tree.
-* **Restoration via memfd**: During restoration, CRIU uses the `memfd_create()` system call to create an anonymous, RAM-backed file.
- * One process (the designated "master" for that specific `shmid`) creates the `memfd`, populates it with the memory contents captured in `pages-shmem.img`, and maps it.
- * All other processes that shared the original region map the same `memfd` file descriptor, ensuring that any subsequent writes are visible to all participants, just as they were before the checkpoint.
-
-### 2. Shared File Mappings
-Created via `mmap(..., MAP_SHARED, fd, ...)`, these regions are backed by a regular file on the filesystem.
-
-* **Mechanism**: CRIU records the file's unique identity (device and inode), the offset within the file, and the mapping length.
-* **Restoration**: Each process re-opens the original file (or a restored version of it) and calls `mmap()` with the `MAP_SHARED` flag. The Linux kernel's standard page cache mechanism automatically handles the sharing and synchronization between processes.
-
-### 3. System V IPC Shared Memory
-Managed via the legacy `shmget()` and `shmat()` APIs, these segments are part of the kernel's IPC subsystem.
-
-* **Mechanism**: CRIU captures the segment's metadata (key, ID, permissions, size) and its full data contents during the dump.
-* **Restoration**: CRIU recreates the IPC segments using `shmget()` with the original parameters and repopulates the data. The restored processes then attach to these segments using `shmat()`, ensuring that IPC-based communication continues seamlessly.
-
-## Advanced Coordination Features
-
-CRIU leverages modern kernel features to handle complex sharing accurately:
-* **kcmp**: Used to definitively verify if two memory mappings in different processes refer to the same underlying kernel object (via `KCMP_VM`), ensuring that shared resources are only dumped once.
-* **Futex Synchronization**: During restoration, CRIU uses futexes to coordinate between the "master" process (which populates shared memory) and "slave" processes, ensuring that no process starts execution until the shared memory state is fully consistent.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md)
-* [Kcmp Trees](kcmp-trees.md)
diff --git a/Documentation/under-the-hood/sockets.md b/Documentation/under-the-hood/sockets.md
deleted file mode 100644
index 60cdaaaaa..000000000
--- a/Documentation/under-the-hood/sockets.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# Network Sockets
-
-CRIU provides extensive support for checkpointing and restoring a wide variety of Linux network sockets, including Unix domain sockets, IPv4/IPv6 (TCP, UDP, RAW), Netlink, and Packet sockets.
-
-## Key Information Captured
-
-To faithfully restore a socket, CRIU must capture its full kernel state:
-1. **Identity**: Family (AF_INET, AF_UNIX, etc.), type (SOCK_STREAM, SOCK_DGRAM), and protocol (TCP, UDP, etc.).
-2. **Addresses**: Local binding addresses and, for connected sockets, the remote peer address and port.
-3. **Socket Options**: A wide range of options (e.g., `SO_KEEPALIVE`, `SO_REUSEADDR`, `TCP_NODELAY`, buffer sizes) are captured and reapplied.
-4. **Queues**: Data currently residing in the send and receive buffers is extracted and re-injected upon restoration.
-5. **State**: Whether the socket is listening, connected, or in a transitional state (like `FIN_WAIT` or `CLOSE_WAIT` for TCP).
-
-## The Dumping Process
-
-CRIU combines information from multiple sources to build a complete picture of each socket.
-
-### 1. sock_diag
-The primary source of truth is the **sock_diag** kernel module. CRIU sends Netlink requests to `sock_diag` to retrieve detailed internal state for most socket families. This provides protocol-level information that is not available via standard userspace APIs.
-
-### 2. SCM_RIGHTS and Parasite
-For deeper inspection—such as peeking at socket queues or enabling TCP repair mode—CRIU uses its **parasite code** to send the actual socket file descriptor to the CRIU process via a Unix domain socket using the `SCM_RIGHTS` mechanism. This allows the CRIU coordinator to perform `ioctl`, `getsockopt`, and `recv(MSG_PEEK)` calls directly on a local copy of the socket.
-
-## Restoration Strategies
-
-### TCP Repair Mode
-Restoring a TCP connection without disrupting the peer (and without sending any packets) is a major challenge. CRIU uses a specialized kernel feature called **TCP Repair Mode**:
-1. CRIU creates a new socket and immediately puts it into repair mode.
-2. While in this mode, CRIU can manually set the sequence numbers, window sizes, and other protocol-level state to match the captured dump.
-3. It populates the send and receive queues with the dumped data.
-4. Finally, it takes the socket out of repair mode, allowing the connection to resume as if it were never interrupted.
-
-### Unix Sockets and SCM_RIGHTS
-Unix sockets are unique because they can be used to transfer other file descriptors. CRIU captures these "in-flight" descriptors (files that have been sent but not yet received) and ensures they are correctly re-queued for the restored process.
-
-## Supported Socket Families
-
-* **AF_UNIX**: Full support for Stream, Datagram, and Sequential Packet types, including abstract and file-backed names.
-* **AF_INET / AF_INET6**:
- * **TCP**: Full connection state restoration via Repair Mode.
- * **UDP / UDPLITE**: Captures addresses, options, and queues.
- * **RAW**: Captures protocol settings and binding state.
-* **AF_NETLINK**: Captures the state of Netlink sockets used for kernel communication (e.g., for routing or audit).
-* **AF_PACKET**: Supports capturing packet filters (BPF) and specific interface bindings.
-
-## See also
-* [TCP Connection Details](tcp-connection.md)
-* [Unix Sockets and SCM_RIGHTS](unix-sockets.md)
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Kerndat Feature Detection](kerndat.md)
diff --git a/Documentation/under-the-hood/stages-of-restoring.md b/Documentation/under-the-hood/stages-of-restoring.md
deleted file mode 100644
index f29692d16..000000000
--- a/Documentation/under-the-hood/stages-of-restoring.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Stages of Restoration
-
-Restoring a complex process tree is a multi-step operation coordinated by a central CRIU process and executed across the newly created process tree. Each stage is synchronized to ensure that dependencies (such as shared files and parent-child relationships) are met and security invariants are maintained.
-
-## The Synchronization Mechanism
-
-CRIU uses a global state machine (defined as `CR_STATE_*` constants) to coordinate between the main CRIU process and the tasks being restored. Tasks use **futexes** in shared memory to signal the completion of their work in each stage and wait for the coordinator to signal the transition to the next stage.
-
-## Stage 1: Root Task Initiation (`CR_STATE_ROOT_TASK`)
-The main CRIU process performs initial image analysis, resolves shared resources, and prepares the restorer code blobs. It then forks the **root task** of the tree being restored. The root task performs initial pre-checks and begins its environmental setup.
-
-## Stage 2: Namespace Preparation (`CR_STATE_PREPARE_NAMESPACES`)
-The root task (and specialized helpers) initializes the required namespaces (Mount, Network, IPC, UTS, Time). This ensures that all subsequent processes in the tree are created within the correct containerized environment from the moment of their birth.
-
-## Stage 3: Process Tree Forking (`CR_STATE_FORKING`)
-The process tree is recursively forked until all processes are recreated.
-* **PID Restoration**: Processes are created with their original PIDs using the `clone3()` system call or the `ns_last_pid` interface.
-* **Transport Setup**: Each task creates an abstract Unix domain socket to "receive" shared file descriptors from its designated "master" peers.
-
-## Stage 4: Main Resource Restoration (`CR_STATE_RESTORE`)
-This is the primary stage where the bulk of the application state is reconstructed:
-* **Files and Sockets**: File descriptors are opened locally or received via `SCM_RIGHTS`.
-* **Memory Mapping**: VMAs are recreated via `mmap()`.
-* **Restorer Jump**: Each task "morphs" by jumping from the main CRIU code into the freestanding **Restorer PIE** blob.
-* **Threads**: Individual application threads are recreated within each process.
-
-## Stage 5: Signal Synchronization (`CR_STATE_RESTORE_SIGCHLD`)
-Tasks restore their original `SIGCHLD` handlers. This stage serves as a critical synchronization point to transition from CRIU's internal error-tracking (which relies on `SIGCHLD` to detect failed restoration steps in children) to the application's original signal handling logic.
-
-## Stage 6: Security and Credentials (`CR_STATE_RESTORE_CREDS`)
-For security reasons, this is the final stage before the application resumes execution. CRIU ensures that sensitive attributes are restored in a specific order:
-1. **Credentials**: UIDs, GIDs, and Capability sets are applied.
-2. **Seccomp**: Security filters are enabled only after the final credentials are in place.
-3. **Process Attributes**: The "dumpable" status and parent-death signals (`pdeath_sig`) are re-established.
-
-By delaying these steps until the very end, CRIU prevents potential security vulnerabilities where a partially-restored process could be intercepted or manipulated while in a transitional state.
-
-## Stage 7: Resumption (`CR_STATE_COMPLETE`)
-The tasks execute their final `sigreturn()` call from within the Restorer PIE. This restores the original register state (including the instruction pointer) and jumps the CPU back into the application's code. The process tree is now fully restored and running.
-
-## See also
-* [Restorer Context](restorer-context.md)
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [PID Restoration](pid-restore.md)
diff --git a/Documentation/under-the-hood/tcp-connection.md b/Documentation/under-the-hood/tcp-connection.md
deleted file mode 100644
index 50969a590..000000000
--- a/Documentation/under-the-hood/tcp-connection.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# TCP Connection Checkpoint and Restore
-
-Checkpointing and restoring established TCP connections is one of CRIU's most advanced features. It allows migrating live applications without dropping active network sessions, provided that the network infrastructure (such as IP routing, virtual IPs, or NAT) supports the transition.
-
-## The Challenge
-
-Standard TCP is managed entirely by the kernel's network stack. Under normal circumstances, userspace cannot:
-1. Read or set internal sequence numbers.
-2. Directly populate the kernel's send and receive buffers.
-3. Transition a socket between states (e.g., from `SYN_SENT` to `ESTABLISHED`) without performing an actual network handshake.
-
-Attempting to restore a connection without specific kernel support would lead to immediate sequence number mismatches and connection resets (RST) from the remote peer.
-
-## The Solution: TCP Repair Mode
-
-To address these limitations, CRIU developers implemented **TCP Repair Mode** in the Linux kernel. When a socket is placed into repair mode, the TCP state machine is suspended, and the kernel allows userspace to manipulate its internal parameters directly.
-
-### Checkpointing (Dumping)
-1. **Network Locking**: Before capturing the socket state, CRIU "locks" the connection using **iptables** or **nftables**. This ensures the kernel drops any incoming packets from the peer, preventing the connection state from changing while CRIU is performing the dump.
-2. **Enable Repair**: CRIU puts the socket into repair mode (`TCP_REPAIR`).
-3. **State Capture**: Using the `libsoccr` library, CRIU extracts:
- * **Sequence Numbers**: The current positions in the data stream (`TCP_QUEUE_SEQ`).
- * **TCP Options**: Window scaling factors, timestamps, and SACK settings (`TCP_REPAIR_OPTIONS`).
- * **Window Parameters**: Send and receive window sizes and offsets (`TCP_REPAIR_WINDOW`).
- * **Queue Data**: The actual bytes currently residing in the kernel's send and receive buffers.
-4. **Silent Close**: Once the state is captured, the socket is closed while still in repair mode. This is crucial as it prevents the kernel from sending `FIN` or `RST` packets to the peer, keeping the connection "alive" from the peer's perspective.
-
-### Restoration
-1. **Socket Creation**: CRIU creates a new socket and immediately enables repair mode.
-2. **Binding**: The socket is bound to the original local IP address and port.
-3. **State Injection**: captured parameters (sequences, windows, options) are applied to the new socket using `setsockopt`.
-4. **Queue Re-population**: The send and receive buffers are re-filled with the original data.
-5. **Activation**: CRIU takes the socket out of repair mode. The kernel now considers the connection to be in the exact state it was at the moment of the checkpoint.
-6. **Network Unlocking**: Finally, the network locks are removed. The application resumes, and the next packet sent or received will have perfectly consistent sequence numbers.
-
-## Network Locking Methods
-
-CRIU supports multiple strategies to manage the network during migration:
-* **nftables** (Preferred): Uses the modern `nft` API to create efficient, temporary rules.
-* **iptables**: Uses traditional `iptables` commands to drop packets for the specific 4-tuple.
-* **Skip**: Allows external orchestration (e.g., by an SDN controller) to handle packet buffering and redirection.
-
-## See also
-* [Network Sockets](sockets.md)
-* [Changing IP Addresses](change-ip-address.md)
-* [Kerndat Feature Detection](kerndat.md)
diff --git a/Documentation/under-the-hood/technologies.md b/Documentation/under-the-hood/technologies.md
deleted file mode 100644
index 75a937588..000000000
--- a/Documentation/under-the-hood/technologies.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Foundational Technologies
-
-CRIU relies on a wide array of advanced Linux kernel features and userspace libraries to perform transparent checkpoint and restore.
-
-## Kernel Technologies
-
-### Core C/R Capabilities
-* **kcmp()**: A system call used to identify shared resources (files, memory mappings, namespaces) between processes by performing internal kernel pointer comparisons.
-* **clone3()**: A modern process creation interface that allows CRIU to atomically request specific PIDs and TIDs, even across nested PID namespaces.
-* **prctl() Extensions**:
- * `PR_SET_MM`: Allows the restorer to reconstruct a process's original memory layout (code, data, heap, etc.).
- * `PR_GET_TID_ADDRESS`: Captures the address used for `set_tid_address`.
- * `PR_SET_THP_DISABLE`: Preserves the status of Transparent Huge Pages.
-* **ptrace() Extensions**:
- * `PTRACE_SEIZE` & `PTRACE_INTERRUPT`: Enables non-disruptive task stopping.
- * `PTRACE_GETSIGMASK` & `PTRACE_SETSIGMASK`: Captures and restores thread signal masks.
- * `PTRACE_PEEKSIGINFO`: Reads pending signal queues without delivering them.
- * `PTRACE_GET_RSEQ_CONF`: Retrieves Restartable Sequences (rseq) registration details.
-
-### Resource Introspection
-* **/proc Filesystem**:
- * `/proc/$pid/map_files`: Provides stable handles to files mapped into a process's memory.
- * `/proc/$pid/fdinfo`: Exposes internal state for file descriptors, including positions, flags, and socket handles.
- * `ioctl(PAGEMAP_SCAN)`: Efficiently identifies dirty and present pages in large address spaces.
-* **sock_diag**: A netlink-based interface used to retrieve detailed protocol-level state for sockets (TCP, UDP, Unix, etc.).
-
-### Advanced Subsystems
-* **TCP Repair Mode**: A specialized socket state that allows CRIU to capture and restore the full internal state of TCP connections without sending network packets.
-* **Userfaultfd**: Enables **Lazy Migration** by allowing CRIU to handle page faults in userspace and load memory pages on-demand.
-* **Mount V2 APIs**: Uses `fsopen()`, `fsmount()`, `open_tree()`, and `move_mount()` to robustly reconstruct complex filesystem hierarchies and propagation groups.
-* **Netfilter (nftables/iptables)**: Used to "lock" network connections during migration to prevent state changes.
-
-## Userspace Technologies
-
-### Compel
-[Compel](../compel.md) is a dedicated sub-project that provides the infrastructure for **Parasite Injection**. It allows CRIU to execute self-contained code (PIE) within the context of a target process to capture internal state.
-
-### Google Protocol Buffers (protobuf)
-CRIU uses [Protocol Buffers](https://developers.google.com/protocol-buffers/) as the standard serialization format for all image files. This ensures a structured, extensible, and cross-version compatible way to store process state.
-
-### ZDTM (Zero-Downtime Migration)
-[ZDTM](zdtm-test-suite.md) is CRIU's comprehensive test suite. It includes hundreds of tests that verify the functional correctness of C/R across various architectures and kernel versions.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Memory Changes Tracking](memory-changes-tracking.md)
-* [Mount V2](mount-v2.md)
-* [PID Restoration](pid-restore.md)
diff --git a/Documentation/under-the-hood/ttys.md b/Documentation/under-the-hood/ttys.md
deleted file mode 100644
index d146e935f..000000000
--- a/Documentation/under-the-hood/ttys.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# TTY (Teletype) Support
-
-CRIU provides support for checkpointing and restoring various types of Linux terminals (TTYs), with a primary focus on **Unix98 Pseudoterminals (PTYs)**.
-
-## Key Information Captured
-
-For each TTY instance, CRIU captures a comprehensive set of kernel metadata:
-1. **Identity**: The TTY type (PTY, Console, Serial, or Virtual Terminal), subtype (Master or Slave), and its unique kernel index.
-2. **Configuration**: Detailed `termios` settings (baud rate, parity, control characters) and window size parameters (`winsize`).
-3. **Ownership and Permissions**: The original UID/GID and mode of the TTY device node.
-4. **Process Context**: Controlling terminal status, Session ID (SID), and Foreground Process Group (PGRP).
-5. **Extended State**: Lock status (`TIOCGLCKTRMIOS`), exclusive mode settings, and packet mode (`TIOCPKT`) flags.
-
-## The PTY Index Challenge
-
-A major challenge in restoring PTYs is that the Linux kernel assigns indices (e.g., the `N` in `/dev/pts/N`) sequentially when `/dev/ptmx` is opened. Standard userspace APIs do not allow requesting a specific index.
-
-### The "Sequential Opening" Strategy
-To ensure each PTY is restored with its original index, CRIU employs a specialized "brute-force" technique:
-1. **Looping Open**: CRIU enters a loop, repeatedly calling `open("/dev/ptmx")`.
-2. **Index Verification**: After each open, it queries the assigned index using the `TIOCGPTN` ioctl.
-3. **Consuming Indices**: If the assigned index is lower than the target index, CRIU **keeps the file descriptor open**. This prevents the kernel from reassigning that index.
-4. **Target Match**: Once the kernel assigns the correct original index, CRIU uses that descriptor as the restored master PTY.
-5. **Cleanup**: All "placeholder" descriptors opened during the loop are then closed, freeing those indices for the rest of the system.
-
-## Restoration Workflow
-
-1. **Master Peer Reconstruction**: A designated process recreates the master PTY using the sequential opening strategy.
-2. **Slave Peer Attachment**: Slave processes open the corresponding `/dev/pts/N` devices. Because the master was created with the correct index, these slaves automatically link to the correct peer.
-3. **State Application**: Termios, window sizes, and device ownership are applied to the newly opened descriptors.
-4. **Controlling Terminal Re-binding**: CRIU re-establishes the relationship between each process and its controlling terminal using the `TIOCSCTTY` ioctl.
-
-## Current Limitations
-
-* **Buffered Data**: Captured TTY input and output queues (data that was sent but not yet read) are currently not fully restored. CRIU ensures the *interface* is restored, but the application may see a reset of buffered streams.
-* **Legacy BSD PTYs**: Support for older BSD-style PTYs is not implemented, as the modern Linux kernel does not provide the necessary introspection to reliably pair these devices.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Descriptor Assignment](how-to-assign-needed-file-descriptor-to-a-file.md)
-* [Kerndat Feature Detection](kerndat.md)
diff --git a/Documentation/under-the-hood/tun-tap.md b/Documentation/under-the-hood/tun-tap.md
deleted file mode 100644
index 5bdedf502..000000000
--- a/Documentation/under-the-hood/tun-tap.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# TUN/TAP Interface Support
-
-CRIU supports checkpointing and restoring Linux TUN/TAP virtual network interfaces. These devices are frequently used in VPN clients, virtualization platforms (like QEMU), and container networking.
-
-## How CRIU Handles TUN/TAP
-
-CRIU manages TUN/TAP as a combination of a **Network Interface** (the link visible to the kernel) and a **File Descriptor** (the handle used by the application to send and receive packets).
-
-### 1. Checkpointing (Dumping)
-During a dump, CRIU identifies TUN/TAP descriptors and collects their full kernel state:
-* **Device Attributes**: The interface name, type (TUN for L3 or TAP for L2), and operational flags (e.g., `IFF_NO_PI`, `IFF_VNET_HDR`).
-* **Persistency**: Whether the device is persistent (`IFF_PERSIST`), meaning it survives even when no process has it open.
-* **Buffer Sizes**: Captures the send buffer size (`TUNGETSNDBUF`) and the virtual network header size (`TUNGETVNETHDRSZ`).
-* **Multi-Queue State**: CRIU identifies if multiple file descriptors are attached to different queues of the same TUN/TAP device, allowing for parallel I/O.
-* **Ownership**: Captures the UID and GID associated with the TUN/TAP device.
-
-### 2. Restoration
-To recreate the TUN/TAP environment exactly as it was, CRIU performs the following:
-* **Interface Creation**: Recreates the virtual link or attaches to an existing persistent one using `TUNSETIFF`.
-* **Index Preservation**: Uses the `TUNSETIFINDEX` ioctl to ensure the restored interface has the exact same numeric index as the original. This is critical for applications that have cached the interface index.
-* **Queue Re-attachment**: For multi-queue devices, CRIU uses `TUNSETIFF` in combination with `TUNSETQUEUE` to correctly re-link each restored file descriptor to its original queue.
-* **State Application**: Restores the original buffer sizes, ownership, and persistent status. If a device was not originally persistent, CRIU explicitly drops the persistency after the application has attached to it.
-
-## Current Limitations
-
-* **Packet Filters (BPF)**: Capturing a TAP interface with a complex BPF filter attached is currently **not supported**. The kernel does not provide a robust way to extract the filter program and re-attach it during restoration without the original application context.
-* **In-flight Packets**: Data currently residing in the kernel's internal TUN/TAP queues (packets sent by the application but not yet processed by the virtual device, or vice versa) is not preserved across a checkpoint.
-
-## See also
-* [Network Sockets](sockets.md)
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Kerndat Feature Detection](kerndat.md)
diff --git a/Documentation/under-the-hood/unix-sockets.md b/Documentation/under-the-hood/unix-sockets.md
deleted file mode 100644
index eff87dd4c..000000000
--- a/Documentation/under-the-hood/unix-sockets.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Unix Domain Sockets
-
-CRIU supports checkpointing and restoring Unix domain sockets (AF_UNIX), including Stream, Datagram, and Sequential Packet types. Unix sockets are unique because they can be bound to paths in the filesystem and are frequently used to transfer file descriptors between processes.
-
-## Key Challenges
-
-1. **Path and Inode Decoupling**: A Unix socket's address (its bind path) and the actual socket file on disk are not intrinsically linked in the kernel. If a socket file is moved or unlinked after `bind()`, the socket still reports its original address.
-2. **In-flight Descriptors**: Unix sockets can contain "in-flight" file descriptors sent via `SCM_RIGHTS` that have been sent by one process but not yet received by the peer.
-3. **Cross-Namespace Bindings**: Sockets in one mount namespace may be bound to paths that are only visible or reachable from another mount namespace.
-
-## CRIU's Solution: `SIOCUNIXFILE`
-
-To reliably restore Unix sockets, CRIU developers upstreamed the `SIOCUNIXFILE` ioctl to the Linux kernel. This ioctl allows CRIU to:
-* Retrieve an `O_PATH` file descriptor to the actual socket file on disk, regardless of its current name or overmounting status in the filesystem.
-* By obtaining this `O_PATH` descriptor, CRIU can definitively identify the exact mount point and inode of the socket file, ensuring it can be recreated in the correct location during restoration.
-
-## Dumping Workflow
-
-1. **Identity and State**: CRIU uses the `sock_diag` netlink interface to retrieve the socket's type, state, and peer ID.
-2. **Peer Linking**: For connected or related sockets (like those created via `socketpair()`), CRIU uses the peer information to link them together in the process tree model.
-3. **File Handle Retrieval**: For sockets bound to the filesystem, CRIU uses `SIOCUNIXFILE` to get a handle to the socket file and records its location.
-4. **Queue and FD Capture**: Send and receive queues are peeked to capture pending data. Crucially, any file descriptors currently residing in the socket's queues are also captured and dumped.
-
-## Restoration Workflow
-
-1. **Socket Creation**: CRIU recreate the socket using the original family, type, and protocol.
-2. **Address Binding**:
- * CRIU creates a temporary "yard" (a `tmpfs` mount) to safely recreate socket files without interfering with the host filesystem.
- * It creates the required directory structure and uses symlinks to ensure the `bind()` call targets the correct path.
-3. **Peer Connection**: For connected stream sockets, one peer performs a `bind()` and `listen()`, while the other calls `connect()`. CRIU's file restoration engine coordinates this to ensure the server end is ready before the client attempts to connect.
-4. **State and Data Injection**: Socket options and pending data are restored.
-5. **Descriptor Redelivery**: In-flight file descriptors are re-injected into the socket's queue using the `SCM_RIGHTS` mechanism, ensuring the application receives them upon resumption.
-
-## External Unix Sockets
-
-If a socket is connected to a process *outside* the tree being checkpointed, CRIU cannot capture the peer's state. These are **External Sockets**.
-* Restoration will fail by default for these sockets to prevent inconsistent states.
-* Users can explicitly allow these connections using the `--external unix[ID]` option, which tells CRIU to treat the socket as a persistent external dependency.
-
-## See also
-* [Network Sockets](sockets.md)
-* [External UNIX socket](external-unix-socket.md)
-* [Descriptor Assignment](how-to-assign-needed-file-descriptor-to-a-file.md)
-* [Kerndat Feature Detection](kerndat.md)
diff --git a/Documentation/under-the-hood/userfaultfd.md b/Documentation/under-the-hood/userfaultfd.md
deleted file mode 100644
index 9a5060454..000000000
--- a/Documentation/under-the-hood/userfaultfd.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Userfaultfd and Lazy Migration
-
-**Userfaultfd** is a powerful Linux kernel feature that allows a userspace process (a "monitor") to handle page faults for other processes. CRIU leverages this feature to implement **Lazy Migration**, which significantly reduces the initial downtime when migrating memory-intensive applications.
-
-## Lazy Migration Overview
-
-In a traditional migration, the destination host must receive the entire memory dump (potentially many gigabytes) before the application can resume. This "freeze time" can be several seconds or even minutes for large applications.
-
-With **Lazy Migration**:
-1. CRIU captures only the minimal process state (registers, file descriptors, etc.) and essential memory pages.
-2. The process tree is resumed immediately on the destination host with most of its memory regions mapped but empty.
-3. Memory pages are transferred from the source host only when the application actually tries to access them ("on-demand").
-
-## How it Works: The Lazy Pages Daemon
-
-CRIU implements lazy migration through a dedicated background process called the **Lazy Pages Daemon**.
-
-### 1. The Handover
-During the restoration process, each process in the tree:
-* Opens a `userfaultfd` file descriptor.
-* Registers its memory regions with the kernel for tracking.
-* Sends the descriptor to the Lazy Pages Daemon via a Unix domain socket using the `SCM_RIGHTS` mechanism.
-* Resumes execution of the application code via `sigreturn`.
-
-### 2. Handling Page Faults
-When the application accesses a page that hasn't been loaded yet, the kernel pauses the faulting thread and sends a message to the Lazy Pages Daemon.
-1. **Notification**: The daemon receives a `UFFD_EVENT_PAGEFAULT` message containing the faulting address.
-2. **Retrieval**: The daemon fetches the required page contents, either from the local `pages.img` images or from a remote **Page Server** on the source host.
-3. **Injection**: The daemon uses the `UFFDIO_COPY` (to fill data) or `UFFDIO_ZEROPAGE` (to fill zeros) ioctl to inject the page into the application's address space.
-4. **Resumption**: Once the kernel confirms the page is filled, it automatically resumes the paused thread.
-
-## Advanced Features: Non-Cooperative UFFD
-
-CRIU utilizes "non-cooperative" kernel features to maintain consistency if the application modifies its memory layout while being lazily restored:
-* **UFFD_FEATURE_EVENT_FORK**: If the process calls `fork()`, the kernel notifies the daemon, which then begins monitoring the new child process.
-* **UFFD_FEATURE_EVENT_REMAP**: If the process moves memory using `mremap()`, the daemon updates its internal mapping table to ensure it continues to fetch the correct data for the new addresses.
-* **UFFD_FEATURE_EVENT_UNMAP / REMOVE**: Handles scenarios where the application releases memory.
-
-## Benefits and Trade-offs
-
-* **Reduced Downtime**: Applications resume in milliseconds, regardless of their total memory size.
-* **Network Jitter**: The application may experience minor stalls (latency spikes) during the initial phase as pages are fetched over the network.
-* **Source Dependency**: The source host and the Page Server must remain alive and connected until the entire memory state has been successfully transferred to the destination.
-
-## See also
-* [Memory Dumping and Restoring](memory-dumping-and-restoring.md)
-* [Page Server](page-server.md)
-* [Kerndat Feature Detection](kerndat.md)
diff --git a/Documentation/under-the-hood/validate-files-on-restore.md b/Documentation/under-the-hood/validate-files-on-restore.md
deleted file mode 100644
index 726558768..000000000
--- a/Documentation/under-the-hood/validate-files-on-restore.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# File Validation on Restore
-
-CRIU can verify that regular files and shared libraries being restored on the destination host are identical to the ones captured during the checkpoint. This is a critical security and stability feature, as mismatching libraries (e.g., a different version of `libc.so`) can lead to immediate application crashes or subtle data corruption due to changed offsets and symbols.
-
-## How File Validation Works
-
-File validation is managed via the `--file-validation` option. CRIU automatically captures metadata for all regular, file-backed mappings during the dump and stores it in the image files.
-
-### Supported Validation Methods
-
-CRIU supports two primary methods for validating files:
-
-#### 1. Build-ID (Default)
-Most modern Linux executables and shared libraries include a **GNU Build-ID**—a unique, compiler-generated hash stored in a dedicated ELF note section (`NT_GNU_BUILD_ID`).
-* **Dumping**: CRIU identifies ELF files by checking their magic numbers. For each ELF file, it maps at most the first **1 MB** of the file (defined as `BUILD_ID_MAP_SIZE`) and extracts the Build-ID hash.
-* **Restoring**: During restoration, CRIU performs the same extraction on the file residing on the target host. If the resulting hash does not match the one stored in the image, CRIU aborts the restoration to prevent corruption.
-* **Fallback**: If a file is not an ELF or lacks a Build-ID, CRIU automatically falls back to validating the file by its size.
-
-#### 2. File Size (`filesize`)
-A simpler and faster validation method that only compares the total size of the file in bytes.
-* **Advantage**: Minimal overhead as it only requires a `stat()` call.
-* **Disadvantage**: Less reliable than Build-ID, as different versions of a file can occasionally have identical sizes.
-
-## Usage and Configuration
-
-File validation is enabled by default using the `buildid` method. You can explicitly configure the behavior using the `--file-validation` flag:
-
-```bash
-# Explicitly use Build-ID validation
-criu restore --file-validation buildid ...
-
-# Use only file size validation
-criu restore --file-validation filesize ...
-```
-
-## Security and Integrity
-
-File validation ensures that the restored process tree runs against the same binary environment it was captured in. This prevents "library injection" scenarios where an attacker might try to force a restored process to run against malicious versions of its original dependencies. It also ensures that internal pointers (such as function addresses) remain valid, as they are often tied to specific library versions.
-
-## See also
-* [Dumping File Descriptors](dumping-files.md)
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Filesystem Peculiarities](filesystems-pecularities.md)
diff --git a/Documentation/under-the-hood/vdso.md b/Documentation/under-the-hood/vdso.md
deleted file mode 100644
index 5cafc6990..000000000
--- a/Documentation/under-the-hood/vdso.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# vDSO and VVAR Handling
-
-The **vDSO** (virtual Dynamic Shared Object) and **VVAR** (virtual VARiable) areas are specialized memory regions mapped by the Linux kernel into every process. They enable high-performance userspace execution of specific system calls (such as `gettimeofday()` or `clock_gettime()`) by providing direct access to kernel-maintained code and data without the overhead of a full context switch.
-
-## The Challenge of C/R
-
-The vDSO is uniquely challenging for checkpoint/restore because its contents and memory layout are determined by the **host kernel**.
-1. **Address Dependencies**: Applications frequently cache the addresses of vDSO functions. These must remain identical after restoration.
-2. **ABI and Kernel Compatibility**: If a process is migrated to a different kernel version, the vDSO code from the original host might be incompatible with the new host's internal kernel-to-userspace data interfaces.
-
-## CRIU's Restoration Strategy
-
-CRIU uses two primary strategies to handle vDSO migration, automatically selecting the best one based on kernel capabilities detected during the [Kerndat](kerndat.md) phase.
-
-### 1. The Proxy (Patching) Method
-This is the fallback approach used when the kernel does not support mapping the vDSO at an arbitrary address:
-* **Checkpoint**: CRIU captures the original vDSO contents and parses its ELF symbol table to identify the offsets of essential functions (e.g., `__vdso_gettimeofday`, `__vdso_time`).
-* **Restoration**:
- 1. CRIU maps the original vDSO binary at its original virtual address.
- 2. It identifies the **new vDSO** provided by the current host kernel.
- 3. For each essential symbol, CRIU locates the corresponding function in the new vDSO.
- 4. CRIU **patches** the code in the original vDSO with a "trampoline" (a small jump instruction) that redirects execution to the equivalent function in the new host's vDSO.
-* **Result**: The application continues to call the memory addresses it originally linked against, but it transparently executes the code optimized for the current host kernel.
-
-### 2. The `arch_prctl` Method (Modern)
-On modern kernels (v4.18+ for x86_64), CRIU uses a significantly more efficient mechanism:
-* CRIU uses the `arch_prctl()` system call with the `ARCH_MAP_VDSO_64` (or `ARCH_MAP_VDSO_32`) flag to instruct the kernel to map its **current, native** vDSO directly at the application's original virtual address.
-* **Advantage**: This eliminates the complexity of ELF patching and ensures the application always uses the most optimal, native code path for the host kernel.
-
-## VVAR Handling
-
-The **VVAR** area contains the raw data (such as the current clock value) that the vDSO code reads.
-* VVAR is a data-only region and is not executable.
-* CRIU identifies the VVAR mapping during the dump and ensures it is correctly re-established on the destination host, usually adjacent to the restored vDSO.
-* When using the `arch_prctl` method, the kernel automatically manages the associated VVAR mapping when the vDSO is moved.
-
-## See also
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
-* [Kerndat Feature Detection](kerndat.md)
-* [Restorer Context](restorer-context.md)
diff --git a/Documentation/under-the-hood/zombies.md b/Documentation/under-the-hood/zombies.md
deleted file mode 100644
index d84d8944f..000000000
--- a/Documentation/under-the-hood/zombies.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Zombie Processes
-
-CRIU supports checkpointing and restoring **zombie processes** (tasks that have terminated but have not yet been reaped by their parent). These processes are a vital part of a process tree's state, as they maintain exit codes that the parent may eventually need to read.
-
-## How CRIU Handles Zombies
-
-Zombie processes are unique because they have no active memory, no file descriptors, and no CPU state. However, they still occupy an entry in the kernel's process table and maintain an identity via their PID.
-
-### 1. Checkpointing (Dumping)
-During the dump phase, CRIU identifies zombie tasks by checking their state in `/proc/$pid/stat`.
-* **State Capture**: CRIU records the zombie's PID and its original **exit code**.
-* **Minimal Footprint**: Because zombies have no address space, CRIU does not attempt to inject parasite code or dump memory for them.
-
-### 2. Restoration
-Restoring a zombie process involves recreating a task and immediately forcing it into a terminated state without allowing its parent to reap it.
-
-* **The Helper Technique**: CRIU forks a new process using the original PID (via `clone3` or `ns_last_pid`).
-* **Immediate Termination**: This process immediately calls the `exit()` system call with the captured exit code.
-* **Parent Coordination**: The parent process of the zombie (which is also being restored) is managed to ensure it does not accidentally reap the new zombie before the restoration is complete.
-* **Result**: This leaves the new process in the zombie state, perfectly matching the original environment's PID table.
-
-## See also
-* [Process Tree Final States](final-states.md)
-* [PID Restoration](pid-restore.md)
-* [Checkpoint/Restore Architecture](checkpointrestore.md)
diff --git a/GEMINI.md b/GEMINI.md
deleted file mode 100644
index ac0a009b0..000000000
--- a/GEMINI.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# CRIU (Checkpoint/Restore In User-space)
-
-CRIU is a tool for saving the state of a running application to a set of files
-(checkpointing) and restoring it back to a live state. It is primarily used for
-live migration of containers, in-place updates, and fast application startup.
-
-It is implemented as a command-line tool called `criu`. The two primary commands
-are `dump` and `restore`.
-
-- `dump`: Saves a process tree and all its related resources (file
- descriptors, IPC, sockets, namespaces, etc.) into a collection of image
- files.
-- `restore`: Restores processes from image files to the same state they were
- in before the dump.
-
-## Quick Start
-
-To get a feel for `criu`, you can try checkpointing and restoring a simple
-process.
-
-1. **Run a simple process:**
- Open a terminal and run a command that will run for a while. Find its PID.
- ```bash
- sleep 1000 &
- [1] 12345
- ```
-
-2. **Dump the process:**
- As root, use `criu dump` with the process ID (`-t`) and a directory for the
- image files (`-D`).
- ```bash
- sudo criu dump -t 12345 -D /tmp/sleep_images -v4 --shell-job
- ```
- The `sleep` process will no longer be running.
-
-3. **Restore the process:**
- Use `criu restore` to bring the process back to life from the images.
- ```bash
- sudo criu restore -D /tmp/sleep_images -v4 --shell-job
- ```
- The `sleep` process will be running again as if nothing happened.
-
-# For Developers and Contributors
-
-This section contains more technical details about CRIU's internals and
-development process.
-
-## Dump Process
-
-On dump, CRIU uses available kernel interfaces to collect information about
-processes. For properties that can only be retrieved from within the process
-itself, CRIU injects a binary blob (called a "parasite") into the process's
-address space and executes it in the context of one of the process's threads.
-This injection is handled by a subproject called **Compel**.
-
-## Restore Process
-
-On restore, CRIU reads the image files to reconstruct the processes. The goal is
-to restore them to the exact state they were in before the dump. The restore
-process is divided into several stages (defined as `CR_STATE_*` in
-`./criu/include/restorer.h`).
-
-The main `criu` process acts as a coordinator. It first restores resources with
-inter-process dependencies (file descriptors, sockets, shared memory,
-namespaces, etc.). It then forks the process tree and sets up namespaces.
-Finally, it restores process-specific resources like file descriptors and memory
-mappings.
-
-A key step involves a small, self-contained binary called the "restorer". All
-restored processes switch to executing this code, which unmaps the CRIU-specific
-memory and restores the application's original memory mappings. On the final
-step, the restorer calls `sigreturn` on a prepared signal frame to resume the
-process with the state it had at the moment of the dump.
-
-## Compel
-
-Compel is a subproject responsible for generating the binary blobs used for the
-parasite code (for dumping) and the restorer code (for restoring). It provides a
-library for injecting and executing this code within the target process's
-address space. It is a separate project because the logic for generating and
-injecting Position-Independent Executable (PIE) code is complex and
-self-contained.
-
-## Coding Style
-
-The C code in the CRIU project follows the
-[Linux Kernel Coding Style](https://www.kernel.org/doc/html/latest/process/coding-style.html).
-Here are some of the main points:
-
-- **Indentation**: Use tabs, which are set to 8 characters.
-- **Line Length**: The preferred line limit is 80 characters, but it can be
- extended to 120 if it improves code readability.
-- **Braces**:
- - The opening brace for a function goes on a new line.
- - The opening brace for a block (like `if`, `for`, `while`, `switch`) goes
- on the same line.
-- **Spaces**: Use spaces around operators (`+`, `-`, `*`, `/`, `%`, `<`, `>`,
- `=`, etc.).
-- **Naming**: Use descriptive names for functions and variables.
-- **Comments**: Use C-style comments (`/* ... */`). For multi-line comments,
- the preferred format is:
- ```c
- /*
- * This is a multi-line
- * comment.
- */
- ```
-
-## Code Layout
-
-The code is organized into the following directories:
-
-- `./compel`: The Compel sub-project.
-- `./criu`: The main `criu` tool source code.
-- `./images`: Protobuf descriptions for the image files.
-- `./test`: All tests.
-- `./test/zdtm`: The Zero-Downtime Migration (ZDTM) test suite.
-- `./test/zdtm.py`: The executor script for ZDTM tests.
-- `./scripts`: Helper scripts.
-- `./scripts/build`: Docker image files used for CI and cross-compilation
- checks.
-- `./crit`: A tool to inspect and manipulate CRIU image files.
-- `./soccr`: A library for TCP socket checkpoint/restore.
-
-## Tests
-
-The main test suite is ZDTM. Here is an example of how to run a single test:
-
-```bash
-sudo ./test/zdtm.py run -t zdtm/static/env00
-```
-
-Each ZDTM test has three stages: preparation, C/R, and results checks. During
-the test, a process calls `test_daemon()` to signal it is ready for C/R, then
-calls `test_waitsig()` to wait for the C/R stage to complete. After being
-restored, the test checks that all its resources are still in a valid state.
-
-## AI-assisted contributions
-
-Add an `Assisted-by` tag to each commit message, placed after the
-commit message body and before the `Signed-off-by` line:
-
-```
-Assisted-by: AGENT_NAME:MODEL_VERSION
-```
-
-Do not add `Signed-off-by` tags on behalf of the user.
diff --git a/INSTALL.md b/INSTALL.md
deleted file mode 100644
index af0702518..000000000
--- a/INSTALL.md
+++ /dev/null
@@ -1,52 +0,0 @@
-## Building CRIU from source code
-
-First, you need to install compile-time dependencies. Check [Installation dependencies](https://criu.org/Installation#Dependencies) for more info.
-
-To compile CRIU, run:
-```
-make
-```
-This should create the `./criu/criu` executable.
-
-To change the default behaviour of CRIU, the following variables can be passed
-to the make command:
-
- * **NETWORK_LOCK_DEFAULT**, can be set to one of the following
- values: `NETWORK_LOCK_IPTABLES`, `NETWORK_LOCK_NFTABLES`,
- `NETWORK_LOCK_SKIP`. CRIU defaults to `NETWORK_LOCK_IPTABLES`
- if nothing is specified. If another network locking backend is
- needed, `make` can be called like this:
- `make NETWORK_LOCK_DEFAULT=NETWORK_LOCK_NFTABLES`
-
-## Installing CRIU from source code
-
-Once CRIU is built one can easily setup the complete CRIU package
-(which includes executable itself, CRIT tool, libraries, manual
-and etc) simply typing
-```
-make install
-```
-this command accepts the following variables:
-
- * **DESTDIR**, to specify global root where all components will be placed under (empty by default);
- * **PREFIX**, to specify additional prefix for path of every component installed (`/usr/local` by default);
- * **BINDIR**, to specify where to put CRIT tool (`$(PREFIX)/bin` by default);
- * **SBINDIR**, to specify where to put CRIU executable (`$(PREFIX)/sbin` by default);
- * **MANDIR**, to specify directory for manual pages (`$(PREFIX)/share/man` by default);
- * **LIBDIR**, to specify directory where to put libraries (guess the correct path by default).
-
-Thus one can type
-```
-make DESTDIR=/some/new/place install
-```
-and get everything installed under `/some/new/place`.
-
-## Uninstalling CRIU
-
-To clean up previously installed CRIU instance one can type
-```
-make uninstall
-```
-and everything should be removed. Note though that if some variable (**DESTDIR**, **BINDIR**
-and such) has been used during installation procedure, the same *must* be passed with
-uninstall action.
diff --git a/MAINTAINERS b/MAINTAINERS
deleted file mode 100644
index 283ad8660..000000000
--- a/MAINTAINERS
+++ /dev/null
@@ -1,9 +0,0 @@
-Andrey Vagin (chief)
-Mike Rapoport
-Dmitry Safonov <0x7f454c46@gmail.com>
-Adrian Reber
-Pavel Tikhomirov
-Radostin Stoyanov
-Alexander Mikhalitsyn
-
-Pavel Emelyanov (retired)
diff --git a/MAINTAINERS_GUIDE.md b/MAINTAINERS_GUIDE.md
deleted file mode 100644
index 5de8e6cb6..000000000
--- a/MAINTAINERS_GUIDE.md
+++ /dev/null
@@ -1,136 +0,0 @@
-## Introduction
-
-Dear maintainer. Thank you for investing the time and energy to help
-make CRIU as useful as possible. Maintaining a project is difficult,
-sometimes unrewarding work. Sure, you will contribute cool features
-to the project, but most of your time will be spent reviewing patches,
-cleaning things up, documenting, answering questions, justifying design
-decisions - while everyone else will just have fun! But remember -- the
-quality of the maintainers work is what distinguishes the good projects
-from the great. So please be proud of your work, even the unglamorous
-parts, and encourage a culture of appreciation and respect for *every*
-aspect of improving the project -- not just the hot new features.
-
-Being a maintainer is a time consuming commitment and should not be
-taken lightly. This document is a manual for maintainers old and new.
-It explains what is expected of maintainers, how they should work, and
-what tools are available to them.
-
-This is a living document - if you see something out of date or missing,
-speak up!
-
-## What are a maintainer's responsibility?
-
-Part of a healthy project is to have active maintainers to support the
-community in contributions and perform tasks to keep the project running.
-It is every maintainer's responsibility to:
-
- * Keep the community a friendly place
- * Deliver prompt feedback and decisions on pull requests and mailing
- list threads
- * Encourage other members to help each other, especially in cases the
- maintainer is overloaded or feels the lack of needed expertise
- * Make sure the changes made respects the philosophy, design and
- roadmap of the project
-
-## How are decisions made?
-
-CRIU is an open-source project with an open design philosophy. This
-means that the repository is the source of truth for EVERY aspect of the
-project. *If it's part of the project, it's in the repo. It's in the
-repo, it's part of the project.*
-
-All decisions affecting CRIU, big and small, follow the same 3 steps:
-
- * Submit a change. Anyone can do this
-
- * Discuss it. Anyone can and is encouraged to do this
-
- * Accept or decline it. Only maintainers do this
-
-*I'm a maintainer, should I make pull requests / send patches too?*
-
-Yes. Nobody should ever push to the repository directly. All changes
-should be made through submitting (and accepting) the change.
-
-### Two-steps decision making ###
-
-Since CRIU is extremely complex piece of software we try double hard
-not to make mistakes, that would be hard to fix in the future. In order
-to facilitate this, the "final" decision is made in two stages:
-
- * We definitely want to try something out
-
- * We think that the attempt was successful
-
-Respectively, new features get accepted first into the *criu-dev* branch and
-after they have been validated they are merged into the *master* branch. Yet,
-urgent bug fixes may land directly in the master branch. If a change in
-the criu-dev branch is considered to be bad (whatever it means), then it
-can be reverted without propagation to the master branch. Reverting from
-the master branch is expected not to happen at all, but if such an
-extraordinary case occurs, the impact of this step, especially the question
-of backward compatibility, should be considered in the most careful manner.
-
-## Who decides what?
-
-All decisions can be expressed as changes to the repository (either in the
-form of pull requests, or patches sent to the mailing list), and maintainers
-make decisions by merging or rejecting them. Review and approval or
-disagreement can be done by anyone and is denoted by adding a respective
-comment in the pull request. However, merging the change into either branch
-only happens after approvals from maintainers.
-
-In order for a patch to be merged into the criu-dev branch at least two
-maintainers should accept it. In order for a patch to be merged into the
-master branch the majority of maintainers should decide that (then prepare
-a pull request, submit it, etc.).
-
-Overall the maintainer system works because of mutual respect across the
-maintainers of the project. The maintainers trust one another to make
-decisions in the best interests of the project. Sometimes maintainers
-can disagree and this is part of a healthy project to represent the point
-of views of various people. In the case where maintainers cannot find
-agreement on a specific change the role of a Chief Maintainer comes into
-play.
-
-### Chief maintainer
-
-The chief maintainer for the project is responsible for overall architecture
-of the project to maintain conceptual integrity. Large decisions and
-architecture changes should be reviewed by the chief maintainer.
-
-Also the chief maintainer has the veto power on any change submitted
-to any branch. Naturally, a change in the criu-dev branch can be reverted
-after a chief maintainer veto, a change in the master branch must be
-carefully reviewed by the chief maintainer and vetoed in advance.
-
-### How are maintainers added (and removed)?
-
-The best maintainers have a vested interest in the project. Maintainers
-are first and foremost contributors that have shown they are committed to
-the long term success of the project. Contributors wanting to become
-maintainers are expected to be deeply involved in contributing code,
-patches review, and paying needed attention to the issues in the project.
-Just contributing does not make you a maintainer, it is about building trust
-with the current maintainers of the project and being a person that they can
-rely on and trust to make decisions in the best interest of the project.
-
-When a contributor wants to become a maintainer or nominate someone as a
-maintainer, one can submit a "nomination", which technically is the
-respective modification to the `MAINTAINERS` file. When a maintainer feels
-they is unable to perform the required duties, or someone else wants to draw
-the community attention to this fact, one can submit a "(self-)removing"
-change.
-
-The final vote to add or to remove a maintainer is to be approved by the
-majority of current maintainers (with the chief maintainer having veto power
-on that too).
-
-One might have noticed, that the chief maintainer (re-)assignment is not
-regulated by this document. That's true :) However, this can be done. If
-the community decides that the chief maintainer needs to be changed the
-respective "decision making rules" are to be prepared, submitted and
-accepted into this file first.
-
-Good luck!
diff --git a/Makefile b/Makefile
index 66b1e20ae..2a63f0477 100644
--- a/Makefile
+++ b/Makefile
@@ -1,532 +1,322 @@
-__nmk_dir=$(CURDIR)/scripts/nmk/scripts/
-export __nmk_dir
+VERSION_MAJOR := 1
+VERSION_MINOR := 5
+VERSION_SUBLEVEL :=
+VERSION_EXTRA :=
+VERSION_NAME :=
+VERSION_SO_MAJOR := 1
+VERSION_SO_MINOR := 0
+
+export VERSION_MAJOR VERSION_MINOR VERSION_SUBLEVEL VERSION_EXTRA VERSION_NAME
+export VERSION_SO_MAJOR VERSION_SO_MINOR
#
-# No need to try to remake our Makefiles
-Makefile: ;
-Makefile.%: ;
-scripts/%.mak: ;
-$(__nmk_dir)%.mk: ;
+# FIXME zdtm building procedure requires implicit rules
+# so I can't use strict make file mode and drop completely
+# all of implicit rules, so I tuned only .SUFFIXES:
+#
+# In future zdtm makefiles need to be fixed and the line below
+# may be uncommented.
+#
+#MAKEFLAGS := -r -R
#
-# Import the build engine
-include $(__nmk_dir)include.mk
-include $(__nmk_dir)macro.mk
+# Common definitions
+#
-ifeq ($(origin HOSTCFLAGS), undefined)
- HOSTCFLAGS := $(CFLAGS) $(USERCFLAGS)
-endif
+FIND := find
+CSCOPE := cscope
+RM := rm -f
+LD := $(CROSS_COMPILE)ld
+CC := $(CROSS_COMPILE)gcc
+NM := $(CROSS_COMPILE)nm
+SH := bash
+MAKE := make
+OBJCOPY := $(CROSS_COMPILE)objcopy
+
+CFLAGS += $(USERCFLAGS)
#
-# Supported Architectures
-ifneq ($(filter-out x86 arm aarch64 ppc64 s390 mips loongarch64 riscv64,$(ARCH)),)
- $(error "The architecture $(ARCH) isn't supported")
-endif
-
-# The PowerPC 64 bits architecture could be big or little endian.
-# They are handled in the same way.
-ifeq ($(SUBARCH),ppc64)
- error := $(error ppc64 big endian is not yet supported)
-endif
-
+# Fetch ARCH from the uname if not yet set
#
-# Architecture specific options.
-ifeq ($(ARCH),arm)
- ARMV := $(shell echo $(SUBARCH) | sed -nr 's/armv([[:digit:]]).*/\1/p; t; i7')
+ARCH ?= $(shell uname -m | sed \
+ -e s/i.86/i386/ \
+ -e s/sun4u/sparc64/ \
+ -e s/s390x/s390/ \
+ -e s/parisc64/parisc/ \
+ -e s/ppc.*/powerpc/ \
+ -e s/mips.*/mips/ \
+ -e s/sh[234].*/sh/)
- ifeq ($(ARMV),6)
- ARCHCFLAGS += -march=armv6
- endif
-
- ifeq ($(ARMV),7)
- ARCHCFLAGS += -march=armv7-a+fp
- endif
-
- ifeq ($(ARMV),8)
- # Running 'setarch linux32 uname -m' returns armv8l on aarch64.
- # This tells CRIU to handle armv8l just as armv7hf. Right now this is
- # only used for compile testing. No further verification of armv8l exists.
- ARCHCFLAGS += -march=armv7-a
- ARMV := 7
- endif
-
- DEFINES := -DCONFIG_ARMV$(ARMV) -DCONFIG_VDSO_32
-
- PROTOUFIX := y
- # For simplicity - compile code in Arm mode without interwork.
- # We could choose Thumb mode as default instead - but a dirty
- # experiment shows that with 90Kb PIEs Thumb code doesn't save
- # even one page. So, let's stick so far to Arm mode as it's more
- # universal around all different Arm variations, until someone
- # will find any use for Thumb mode. -dima
- CFLAGS_PIE := -marm
+ifeq ($(ARCH),i386)
+ SRCARCH := x86-32
+ DEFINES := -DCONFIG_X86_32
+ VDSO := y
+endif
+ifeq ($(ARCH),x86_64)
+ SRCARCH := x86
+ DEFINES := -DCONFIG_X86_64
+ LDARCH := i386:x86-64
+ VDSO := y
endif
+ifeq ($(shell echo $(ARCH) | sed -e 's/arm.*/arm/'),arm)
+ ARMV := $(shell echo $(ARCH) | sed -nr 's/armv([[:digit:]]).*/\1/p; t; i7')
+ SRCARCH := arm
+ DEFINES := -DCONFIG_ARMV$(ARMV)
+
+ USERCFLAGS += -Wa,-mimplicit-it=always
+
+ ifeq ($(ARMV),6)
+ USERCFLAGS += -march=armv6
+ endif
+
+ ifeq ($(ARMV),7)
+ USERCFLAGS += -march=armv7-a
+ endif
+endif
ifeq ($(ARCH),aarch64)
- DEFINES := -DCONFIG_AARCH64
- CC_MBRANCH_PROT := $(shell $(CC) -c -x c /dev/null -mbranch-protection=none -o /dev/null >/dev/null 2>&1 && echo "-mbranch-protection=none")
- CFLAGS_PIE := $(CC_MBRANCH_PROT)
+ VDSO := y
endif
-ifeq ($(ARCH),ppc64)
- LDARCH := powerpc:common64
- DEFINES := -DCONFIG_PPC64 -D__SANE_USERSPACE_TYPES__
-endif
+SRCARCH ?= $(ARCH)
+LDARCH ?= $(SRCARCH)
-ifeq ($(ARCH),x86)
- LDARCH := i386:x86-64
- DEFINES := -DCONFIG_X86_64
-endif
+SRC_DIR ?= $(CURDIR)
+ARCH_DIR := arch/$(SRCARCH)
-ifeq ($(ARCH),mips)
- DEFINES := -DCONFIG_MIPS
-endif
+$(if $(wildcard $(ARCH_DIR)),,$(error "The architecture $(ARCH) isn't supported"))
-ifeq ($(ARCH),loongarch64)
- DEFINES := -DCONFIG_LOONGARCH64
-endif
+cflags-y += -iquote include -iquote pie -iquote .
+cflags-y += -iquote $(ARCH_DIR) -iquote $(ARCH_DIR)/include
+cflags-y += -fno-strict-aliasing
+export cflags-y
-ifeq ($(ARCH),riscv64)
- DEFINES := -DCONFIG_RISCV64
-endif
+LIBS := -lrt -lpthread -lprotobuf-c -ldl
-#
-# CFLAGS_PIE:
-#
-# Ensure with -fno-optimize-sibling-calls that we don't create GOT
-# (Global Offset Table) relocations with gcc compilers that don't have
-# commit "S/390: Fix 64 bit sibcall".
-ifeq ($(ARCH),s390)
- ARCH := s390
- DEFINES := -DCONFIG_S390
- CFLAGS_PIE := -fno-optimize-sibling-calls
-endif
+DEFINES += -D_FILE_OFFSET_BITS=64
+DEFINES += -D_GNU_SOURCE
-CFLAGS_PIE += -DCR_NOGLIBC
-export CFLAGS_PIE
-
-LDARCH ?= $(ARCH)
-export LDARCH
-export PROTOUFIX DEFINES
-
-#
-# Independent options for all tools.
-DEFINES += -D_FILE_OFFSET_BITS=64
-DEFINES += -D_LARGEFILE64_SOURCE
-DEFINES += -D_GNU_SOURCE
-
-WARNINGS := -Wall -Wformat-security -Wdeclaration-after-statement -Wstrict-prototypes
-
-# -Wdangling-pointer results in false warning when we add a list element to
-# local list head variable. It is false positive because before leaving the
-# function we always check that local list head variable is empty, thus
-# insuring that pointer to it is not dangling anywhere, but gcc can't
-# understand it.
-# Note: There is similar problem with kernel list, where this warning is also
-# disabled: https://github.com/torvalds/linux/commit/49beadbd47c2
-WARNINGS += -Wno-dangling-pointer -Wno-unknown-warning-option
-
-CFLAGS-GCOV := --coverage -fno-exceptions -fno-inline -fprofile-update=atomic
-export CFLAGS-GCOV
-
-ifeq ($(ARCH),mips)
-WARNINGS := -rdynamic
-endif
-
-ifeq ($(ARCH),loongarch64)
-WARNINGS += -Wno-implicit-function-declaration
-endif
-
-ifneq ($(GCOV),)
- LDFLAGS += -lgcov
- CFLAGS += $(CFLAGS-GCOV)
-endif
-
-ifneq ($(NETWORK_LOCK_DEFAULT),)
- CFLAGS += -DNETWORK_LOCK_DEFAULT=$(NETWORK_LOCK_DEFAULT)
-endif
-
-ifeq ($(ASAN),1)
- CFLAGS-ASAN := -fsanitize=address
- export CFLAGS-ASAN
- CFLAGS += $(CFLAGS-ASAN)
-endif
+WARNINGS := -Wall
ifneq ($(WERROR),0)
- WARNINGS += -Werror
+ WARNINGS += -Werror
endif
ifeq ($(DEBUG),1)
- DEFINES += -DCR_DEBUG
- CFLAGS += -O0 -ggdb3
+ DEFINES += -DCR_DEBUG
+ CFLAGS += -O0 -ggdb3
else
- CFLAGS += -O2 -g
+ CFLAGS += -O2
endif
ifeq ($(GMON),1)
- CFLAGS += -pg
- GMONLDOPT += -pg
-export GMON GMONLDOPT
+ CFLAGS += -pg
+ GMONLDOPT = -pg
endif
-AFLAGS += -D__ASSEMBLY__
-CFLAGS += $(USERCFLAGS) $(ARCHCFLAGS) $(WARNINGS) $(DEFINES) -iquote include/
-HOSTCFLAGS += $(WARNINGS) $(DEFINES) -iquote include/
-export AFLAGS CFLAGS USERCLFAGS HOSTCFLAGS
+CFLAGS += $(WARNINGS) $(DEFINES)
+SYSCALL-LIB := $(ARCH_DIR)/syscalls.built-in.o
+ARCH-LIB := $(ARCH_DIR)/crtools.built-in.o
+CRIU-SO := libcriu
+CRIU-LIB := lib/$(CRIU-SO).so
+CRIU-INC := lib/criu.h include/criu-plugin.h include/criu-log.h protobuf/rpc.proto
-# Default target
-all: criu lib crit cuda_plugin
-.PHONY: all
+export CC MAKE CFLAGS LIBS SRCARCH DEFINES MAKEFLAGS CRIU-SO
+export SRC_DIR SYSCALL-LIB SH RM ARCH_DIR OBJCOPY LDARCH LD
+export USERCFLAGS
+export cflags-y
+export VDSO
-#
-# Version headers.
-include Makefile.versions
-
-VERSION_HEADER := criu/include/version.h
-GITID_FILE := .gitid
-GITID := $(shell if [ -d ".git" ]; then git describe --always; fi)
-
-# Git repository wasn't inited in CRIU folder
-ifeq ($(GITID),)
- GITID := 0
-else
- GITID_FILE_VALUE := $(shell if [ -f '$(GITID_FILE)' ]; then if [ `cat '$(GITID_FILE)'` = $(GITID) ]; then echo y; fi; fi)
- ifneq ($(GITID_FILE_VALUE),y)
- .PHONY: $(GITID_FILE)
- endif
-endif
-
-$(GITID_FILE):
- $(call msg-gen, $@)
- $(Q) echo "$(GITID)" > $(GITID_FILE)
-
-$(VERSION_HEADER): Makefile.versions $(GITID_FILE)
- $(call msg-gen, $@)
- $(Q) echo "/* Autogenerated, do not edit */" > $@
- $(Q) echo "#ifndef __CR_VERSION_H__" >> $@
- $(Q) echo "#define __CR_VERSION_H__" >> $@
- $(Q) echo "#define CRIU_VERSION \"$(CRIU_VERSION)\"" >> $@
- $(Q) echo "#define CRIU_VERSION_MAJOR " $(CRIU_VERSION_MAJOR) >> $@
- $(Q) echo "#define CRIU_VERSION_MINOR " $(CRIU_VERSION_MINOR) >> $@
-ifneq ($(CRIU_VERSION_SUBLEVEL),)
- $(Q) echo "#define CRIU_VERSION_SUBLEVEL " $(CRIU_VERSION_SUBLEVEL) >> $@
-endif
-ifneq ($(CRIU_VERSION_EXTRA),)
- $(Q) echo "#define CRIU_VERSION_EXTRA " $(CRIU_VERSION_EXTRA) >> $@
-endif
- $(Q) echo "#define CRIU_GITID \"$(GITID)\"" >> $@
- $(Q) echo "#endif /* __CR_VERSION_H__ */" >> $@
-
-criu-deps += $(VERSION_HEADER)
-
-#
-# Setup proper link for asm headers in common code.
-include/common/asm: include/common/arch/$(ARCH)/asm
- $(call msg-gen, $@)
- $(Q) ln -s ./arch/$(ARCH)/asm $@
-
-criu-deps += include/common/asm
-
-#
-# Configure variables.
-export CONFIG_HEADER := include/common/config.h
-ifeq ($(filter tags etags cscope clean lint indent fetch-clang-format help mrproper,$(MAKECMDGOALS)),)
+include Makefile.inc
include Makefile.config
-else
-# To clean all files, enable make/build options here
-export CONFIG_COMPAT := y
-export CONFIG_GNUTLS := y
-export CONFIG_HAS_LIBBPF := y
-export CONFIG_LZ4 := y
+include scripts/Makefile.version
+include scripts/Makefile.rules
+
+.SUFFIXES:
+
+#
+# shorthand
+build := -r -R -f scripts/Makefile.build makefile=Makefile obj
+build-crtools := -r -R -f scripts/Makefile.build makefile=Makefile.crtools obj
+
+PROGRAM := criu
+
+.PHONY: all zdtm test rebuild clean distclean tags cscope \
+ docs help pie protobuf $(ARCH_DIR) clean-built lib crit
+
+ifeq ($(GCOV),1)
+%.o $(PROGRAM): override CFLAGS += --coverage
endif
-#
-# Protobuf images first, they are not depending
-# on anything else.
-$(eval $(call gen-built-in,images))
-criu-deps += images/built-in.o
+all: config pie $(VERSION_HEADER) $(CRIU-LIB)
+ $(Q) $(MAKE) $(PROGRAM)
+ $(Q) $(MAKE) crit
-#
-# Compel get used by CRIU, build it earlier
-include Makefile.compel
+protobuf/%::
+ $(Q) $(MAKE) $(build)=protobuf $@
+protobuf:
+ $(Q) $(MAKE) $(build)=protobuf all
-#
-# Next the socket CR library
-#
-SOCCR_A := soccr/libsoccr.a
-soccr/Makefile: ;
-soccr/%: $(CONFIG_HEADER) .FORCE
- $(Q) $(MAKE) $(build)=soccr $@
-soccr/built-in.o: $(CONFIG_HEADER) .FORCE
- $(Q) $(MAKE) $(build)=soccr all
-$(SOCCR_A): |soccr/built-in.o
-criu-deps += $(SOCCR_A)
+$(ARCH_DIR)/%:: protobuf config
+ $(Q) $(MAKE) $(build)=$(ARCH_DIR) $@
+$(ARCH_DIR): protobuf config
+ $(Q) $(MAKE) $(build)=$(ARCH_DIR) all
-#
-# CRIU building done in own directory
-# with slightly different rules so we
-# can't use nmk engine directly (we
-# build syscalls library and such).
-#
-# But note that we're already included
-# the nmk so we can reuse it there.
-criu/Makefile: ;
-criu/Makefile.packages: ;
-criu/Makefile.crtools: ;
-criu/%: $(criu-deps) .FORCE
- $(Q) $(MAKE) $(build)=criu $@
-criu: $(criu-deps)
- $(Q) $(MAKE) $(build)=criu all
-.PHONY: criu
+pie/%:: $(ARCH_DIR)
+ $(Q) $(MAKE) $(build)=pie $@
+pie: $(ARCH_DIR)
+ $(Q) $(MAKE) $(build)=pie all
-unittest: $(criu-deps)
- $(Q) $(MAKE) $(build)=criu unittest
-.PHONY: unittest
+%.o %.i %.s %.d: $(VERSION_HEADER) pie
+ $(Q) $(MAKE) $(build-crtools)=. $@
+built-in.o: $(VERSION_HEADER) pie
+ $(Q) $(MAKE) $(build-crtools)=. $@
-
-#
-# Libraries next once criu is ready
-# (we might generate headers and such
-# when building criu itself).
-lib/Makefile: ;
-lib/%: criu .FORCE
+lib/%:: $(VERSION_HEADER) config built-in.o
$(Q) $(MAKE) $(build)=lib $@
-lib: criu
+lib: $(VERSION_HEADER) config built-in.o
$(Q) $(MAKE) $(build)=lib all
-.PHONY: lib
-clean mrproper:
- $(Q) $(MAKE) $(build)=images $@
- $(Q) $(MAKE) $(build)=criu $@
- $(Q) $(MAKE) $(build)=soccr $@
- $(Q) $(MAKE) $(build)=lib $@
- $(Q) $(MAKE) $(build)=crit $@
- $(Q) $(MAKE) $(build)=compel $@
- $(Q) $(MAKE) $(build)=compel/plugins $@
-.PHONY: clean mrproper
+ifeq ($(VDSO),y)
+$(ARCH_DIR)/vdso-pie.o: pie
+ $(Q) $(MAKE) $(build)=pie $(ARCH_DIR)/vdso-pie.o
+PROGRAM-BUILTINS += $(ARCH_DIR)/vdso-pie.o
+ifeq ($(SRCARCH),aarch64)
+PROGRAM-BUILTINS += $(ARCH_DIR)/intraprocedure.o
+endif
+endif
-clean-amdgpu_plugin:
- $(Q) $(MAKE) -C plugins/amdgpu clean
-.PHONY: clean-amdgpu_plugin
+PROGRAM-BUILTINS += pie/util-fd.o
+PROGRAM-BUILTINS += pie/util.o
+PROGRAM-BUILTINS += protobuf/built-in.o
+PROGRAM-BUILTINS += built-in.o
-clean-cuda_plugin:
- $(Q) $(MAKE) -C plugins/cuda clean
-.PHONY: clean-cuda_plugin
+$(SYSCALL-LIB) $(ARCH-LIB) $(PROGRAM-BUILTINS): config
-clean-top:
- $(Q) $(MAKE) -C Documentation clean
- $(Q) $(MAKE) -C soccr/test clean
- $(Q) $(MAKE) $(build)=test/compel clean
- $(Q) $(RM) .gitid
-.PHONY: clean-top
+$(PROGRAM): $(SYSCALL-LIB) $(ARCH-LIB) $(PROGRAM-BUILTINS)
+ $(E) " LINK " $@
+ $(Q) $(CC) $(CFLAGS) $^ $(LIBS) $(LDFLAGS) $(GMONLDOPT) -rdynamic -o $@
-clean: clean-top clean-amdgpu_plugin clean-cuda_plugin
-
-mrproper-top: clean-top clean-amdgpu_plugin clean-cuda_plugin
- $(Q) $(RM) $(CONFIG_HEADER)
- $(Q) $(RM) $(VERSION_HEADER)
- $(Q) $(RM) $(COMPEL_VERSION_HEADER)
- $(Q) $(RM) include/common/asm
- $(Q) $(RM) compel/include/asm
- $(Q) $(RM) cscope.*
- $(Q) $(RM) tags TAGS
-.PHONY: mrproper-top
-
-mrproper: mrproper-top
-
-#
-# Non-CRIU stuff.
-#
-
-docs:
- $(Q) $(MAKE) -s -C Documentation all
-.PHONY: docs
+crit:
+ $(Q) $(MAKE) -C pycriu all
zdtm: all
$(Q) $(MAKE) -C test/zdtm all
-.PHONY: zdtm
test: zdtm
$(Q) $(MAKE) -C test
-.PHONY: test
-amdgpu_plugin: criu
- $(Q) $(MAKE) -C plugins/amdgpu all
-.PHONY: amdgpu_plugin
+clean-built:
+ $(Q) $(RM) $(VERSION_HEADER)
+ $(Q) $(MAKE) $(build)=$(ARCH_DIR) clean
+ $(Q) $(MAKE) $(build)=protobuf clean
+ $(Q) $(MAKE) $(build)=pie clean
+ $(Q) $(MAKE) $(build)=lib clean
+ $(Q) $(MAKE) $(build-crtools)=. clean
+ $(Q) $(MAKE) -C Documentation clean
+ $(Q) $(RM) ./include/config.h
+ $(Q) $(RM) ./$(PROGRAM)
-cuda_plugin: criu
- $(Q) $(MAKE) -C plugins/cuda all
-.PHONY: cuda_plugin
+rebuild: clean-built
+ $(E) " FORCE-REBUILD"
+ $(Q) $(MAKE)
-crit: lib
- $(Q) $(MAKE) -C crit
-.PHONY: crit
+clean: clean-built
+ $(E) " CLEAN"
+ $(Q) $(RM) ./*.img
+ $(Q) $(RM) ./*.out
+ $(Q) $(RM) ./*.bin
+ $(Q) $(RM) ./*.gcov ./*.gcda ./*.gcno
+ $(Q) $(RM) -r ./gcov
+ $(Q) $(RM) protobuf-desc-gen.h
+ $(Q) $(MAKE) -C test $@
+ $(Q) $(MAKE) -C pycriu $@
+ $(Q) $(RM) ./*.pyc
+ $(Q) $(RM) -r build
-#
-# Generating tar requires tag matched CRIU_VERSION.
-# If not found then simply use GIT's describe with
-# "v" prefix stripped.
-head-name := $(shell git tag -l v$(CRIU_VERSION))
-ifeq ($(head-name),)
- head-name := $(shell git describe 2>/dev/null)
-endif
-# If no git tag could describe current commit,
-# use pre-defined CRIU_VERSION with GITID (if any).
-ifeq ($(head-name),)
- ifneq ($(GITID),)
- head-name := $(CRIU_VERSION)-$(GITID)
- else
- head-name := $(CRIU_VERSION)
- endif
-endif
-tar-name := $(shell echo $(head-name) | sed -e 's/^v//g')
-criu-$(tar-name).tar.bz2:
- git archive --format tar --prefix 'criu-$(tar-name)/' $(head-name) | bzip2 > $@
-dist tar: criu-$(tar-name).tar.bz2 ;
-.PHONY: dist tar
+distclean: clean
+ $(E) " DISTCLEAN"
+ $(Q) $(RM) ./tags
+ $(Q) $(RM) ./cscope*
-TAGS_FILES_REGEXP := . -name '*.[hcS]' ! -path './.*' \( ! -path './test/*' -o -path './test/zdtm/lib/*' \)
tags:
- $(call msg-gen, $@)
+ $(E) " GEN " $@
$(Q) $(RM) tags
- $(Q) $(FIND) $(TAGS_FILES_REGEXP) -print | xargs $(CTAGS) -a
-.PHONY: tags
-
-etags:
- $(call msg-gen, $@)
- $(Q) $(RM) TAGS
- $(Q) $(FIND) $(TAGS_FILES_REGEXP) -print | xargs $(ETAGS) -a
-.PHONY: etags
-
+ $(Q) $(FIND) . -name '*.[hcS]' ! -path './.*' -print | xargs ctags -a
cscope:
- $(call msg-gen, $@)
- $(Q) $(FIND) $(TAGS_FILES_REGEXP) ! -type l -print > cscope.files
+ $(E) " GEN " $@
+ $(Q) $(FIND) . -name '*.[hcS]' ! -path './.*' -print > cscope.files
$(Q) $(CSCOPE) -bkqu
-.PHONY: cscope
-gcov:
- $(E) " GCOV"
- $(Q) test -d gcov || mkdir gcov && \
- geninfo --output-filename gcov/criu.info --no-recursion criu/ && \
- cd gcov && \
- genhtml --rc lcov_branch_coverage=1 --output-directory html criu.info
- @echo "Code coverage report is in `pwd`/gcov/html/ directory."
-.PHONY: gcov
+docs:
+ $(Q) $(MAKE) -s -C Documentation all
-docker-build:
- $(MAKE) -C scripts/build/ x86_64
-.PHONY: docker-build
+dist: tar
+tar: criu-$(CRTOOLSVERSION).tar.bz2
+criu-$(CRTOOLSVERSION).tar.bz2:
+ git archive --format tar --prefix 'criu-$(CRTOOLSVERSION)/' \
+ v$(CRTOOLSVERSION) | bzip2 > $@
+.PHONY: dist tar
-docker-test:
- docker run --rm --privileged -v /lib/modules:/lib/modules --network=host --cgroupns=host criu-x86_64 \
- ./test/zdtm.py run -a --keep-going --ignore-taint
-.PHONY: docker-test
+install: $(PROGRAM) $(CRIU-LIB) install-man install-crit
+ $(E) " INSTALL " $(PROGRAM)
+ $(Q) mkdir -p $(DESTDIR)$(SBINDIR)
+ $(Q) install -m 755 $(PROGRAM) $(DESTDIR)$(SBINDIR)
+ $(Q) mkdir -p $(DESTDIR)$(LIBDIR)
+ $(Q) install -m 755 $(CRIU-LIB) \
+ $(DESTDIR)$(LIBDIR)/$(CRIU-SO).so.$(VERSION_SO_MAJOR).$(VERSION_SO_MINOR)
+ $(Q) ln -fns $(CRIU-SO).so.$(VERSION_SO_MAJOR).$(VERSION_SO_MINOR) \
+ $(DESTDIR)$(LIBDIR)/$(CRIU-SO).so.$(VERSION_SO_MAJOR)
+ $(Q) ln -fns $(CRIU-SO).so.$(VERSION_SO_MAJOR).$(VERSION_SO_MINOR) \
+ $(DESTDIR)$(LIBDIR)/$(CRIU-SO).so
+ $(Q) mkdir -p $(DESTDIR)$(INCLUDEDIR)
+ $(Q) install -m 644 $(CRIU-INC) $(DESTDIR)$(INCLUDEDIR)
+ $(Q) mkdir -p $(DESTDIR)$(SYSTEMDUNITDIR)
+ $(Q) install -m 644 scripts/sd/criu.socket $(DESTDIR)$(SYSTEMDUNITDIR)
+ $(Q) install -m 644 scripts/sd/criu.service $(DESTDIR)$(SYSTEMDUNITDIR)
+ $(Q) mkdir -p $(DESTDIR)$(LOGROTATEDIR)
+ $(Q) install -m 644 scripts/logrotate.d/criu-service $(DESTDIR)$(LOGROTATEDIR)
+ $(Q) sed -e 's,@version@,$(GITID),' \
+ -e 's,@libdir@,$(LIBDIR),' \
+ -e 's,@includedir@,$(dir $(INCLUDEDIR)),' \
+ lib/criu.pc.in > criu.pc
+ $(Q) mkdir -p $(DESTDIR)$(LIBDIR)/pkgconfig
+ $(Q) install -m 644 criu.pc $(DESTDIR)$(LIBDIR)/pkgconfig
+
+install-man:
+ $(Q) $(MAKE) -C Documentation install
+
+install-crit: crit
+ $(E) " INSTALL crit"
+ $(Q) python scripts/crit-setup.py install --prefix=$(DESTDIR)
+
+.PHONY: install install-man install-crit
help:
@echo ' Targets:'
@echo ' all - Build all [*] targets'
@echo ' * criu - Build criu'
- @echo ' * crit - Build crit'
@echo ' zdtm - Build zdtm test-suite'
@echo ' docs - Build documentation'
- @echo ' install - Install CRIU (see INSTALL.md)'
- @echo ' uninstall - Uninstall CRIU'
+ @echo ' install - Install binary and man page'
@echo ' dist - Create a source tarball'
- @echo ' clean - Clean most, but leave enough to navigate'
- @echo ' mrproper - Delete all compiled/generated files'
+ @echo ' clean - Clean everything'
@echo ' tags - Generate tags file (ctags)'
- @echo ' etags - Generate TAGS file (etags)'
@echo ' cscope - Generate cscope database'
+ @echo ' rebuild - Force-rebuild of [*] targets'
@echo ' test - Run zdtm test-suite'
- @echo ' gcov - Make code coverage report'
- @echo ' unittest - Run unit tests'
- @echo ' lint - Run code linters'
- @echo ' indent - Indent C code'
- @echo ' amdgpu_plugin - Make AMD GPU plugin'
- @echo ' cuda_plugin - Make NVIDIA CUDA plugin'
-.PHONY: help
-ruff:
- @ruff --version
- ruff check ${RUFF_FLAGS} --config=scripts/ruff.toml \
- test/zdtm.py \
- test/inhfd/*.py \
- test/others/rpc/config_file.py \
- test/others/action-script/check_actions.py \
- test/others/pycriu/*.py \
- lib/pycriu/criu.py \
- lib/pycriu/__init__.py \
- lib/pycriu/images/pb2dict.py \
- lib/pycriu/images/images.py \
- scripts/criu-ns \
- scripts/magic-gen.py \
- test/others/criu-ns/run.py \
- crit/*.py \
- crit/crit/*.py \
- test/others/crit/*.py \
- scripts/uninstall_module.py \
- coredump/ coredump/coredump \
- scripts/github-indent-warnings.py \
- contrib/criu-service-client/test/*.py \
- contrib/compression-benchmark/ \
- test/others/compression/ \
- soccr/test/run.py \
- soccr/test/tcp-test.py
+gcov:
+ $(E) " GCOV"
+ $(Q) mkdir gcov && \
+ cd gcov && \
+ cp ../*.gcno ../*.c ../test/root/crtools/ && \
+ geninfo --no-checksum --output-filename crtools.l.info --no-recursion .. && \
+ geninfo --no-checksum --output-filename crtools.ns.info --no-recursion ../test/root/crtools && \
+ sed -i 's#/test/root/crtools##' crtools.ns.info && \
+ lcov -a crtools.l.info -a crtools.ns.info -o crtools.info && \
+ genhtml -o html crtools.info
+.PHONY: gcov
-shellcheck:
- shellcheck --version
- shellcheck scripts/*.sh
- shellcheck scripts/ci/*.sh
- shellcheck contrib/apt-install contrib/dependencies/*.sh
- shellcheck -x test/others/crit/*.sh
- shellcheck -x test/others/libcriu/*.sh
- shellcheck -x test/others/crit/*.sh test/others/criu-coredump/*.sh
- shellcheck -x test/others/config-file/*.sh
- shellcheck -x test/others/action-script/*.sh
- shellcheck -x contrib/criu-service-client/test/*.sh
- shellcheck -x test/others/compression/*/*.sh
-
-codespell:
- codespell
-
-lint: ruff shellcheck codespell
- # Do not append \n to pr_perror, pr_pwarn or fail
- ! git --no-pager grep -E '^\s*\<(pr_perror|pr_pwarn|fail)\>.*\\n"'
- # Do not use %m with pr_* or fail
- ! git --no-pager grep -E '^\s*\<(pr_(err|perror|warn|pwarn|debug|info|msg)|fail)\>.*%m'
- # Do not use errno with pr_perror, pr_pwarn or fail
- ! git --no-pager grep -E '^\s*\<(pr_perror|pr_pwarn|fail)\>\(".*".*errno'
- # End pr_(err|warn|msg|info|debug) with \n
- ! git --no-pager grep -En '^\s*\.*);$$' | grep -v '\\n'
- # No EOL whitespace for C files
- ! git --no-pager grep -E '\s+$$' \*.c \*.h
-.PHONY: lint ruff shellcheck codespell
-
-codecov: SHELL := $(shell command -v bash)
-codecov:
- curl -Os https://uploader.codecov.io/latest/linux/codecov
- chmod +x codecov
- ./codecov
-.PHONY: codecov
-
-fetch-clang-format: .FORCE
- $(E) ".clang-format"
- $(Q) scripts/fetch-clang-format.sh
-
-BASE ?= "HEAD~1"
-OPTS ?= "--quiet"
-indent:
- git clang-format --style file --extensions c,h $(OPTS) $(BASE)
-.PHONY: indent
-
-include Makefile.install
-
-.DEFAULT_GOAL := all
-
-# Disable implicit rules in _this_ Makefile.
-.SUFFIXES:
-
-#
-# Optional local include.
--include Makefile.local
+.DEFAULT_GOAL := all
diff --git a/Makefile.compel b/Makefile.compel
deleted file mode 100644
index a4209edc5..000000000
--- a/Makefile.compel
+++ /dev/null
@@ -1,77 +0,0 @@
-COMPEL_BIN := ./compel/compel-host
-export COMPEL_BIN
-
-COMPEL_VERSION_HEADER := compel/include/version.h
-
-$(COMPEL_VERSION_HEADER): Makefile.versions
- $(call msg-gen, $(COMPEL_VERSION_HEADER))
- $(Q) echo "/* Autogenerated, do not edit */" > $(COMPEL_VERSION_HEADER)
- $(Q) echo "#ifndef COMPEL_SO_VERSION_H__" >> $(COMPEL_VERSION_HEADER)
- $(Q) echo "#define COMPEL_SO_VERSION_H__" >> $(COMPEL_VERSION_HEADER)
- $(Q) echo "#define COMPEL_SO_VERSION \"$(COMPEL_SO_VERSION)\"" >> $(COMPEL_VERSION_HEADER)
- $(Q) echo "#define COMPEL_SO_VERSION_MAJOR " $(COMPEL_SO_VERSION_MAJOR) >> $(COMPEL_VERSION_HEADER)
- $(Q) echo "#define COMPEL_SO_VERSION_MINOR " $(COMPEL_SO_VERSION_MINOR) >> $(COMPEL_VERSION_HEADER)
- $(Q) echo "#define COMPEL_SO_VERSION_SUBLEVEL " $(COMPEL_SO_VERSION_SUBLEVEL) >> $(COMPEL_VERSION_HEADER)
- $(Q) echo "#endif /* COMPEL_SO_VERSION_H__ */" >> $(COMPEL_VERSION_HEADER)
-
-compel/include/asm:
- $(call msg-gen, $@)
- $(Q) ln -s ../arch/$(ARCH)/src/lib/include $@
-
-compel-deps += compel/include/asm
-compel-deps += $(COMPEL_VERSION_HEADER)
-compel-deps += $(CONFIG_HEADER)
-compel-deps += include/common/asm
-compel-plugins += compel/plugins/std.lib.a compel/plugins/fds.lib.a
-
-LIBCOMPEL_SO := libcompel.so
-LIBCOMPEL_A := libcompel.a
-export LIBCOMPEL_SO LIBCOMPEL_A
-
-#
-# Compel itself.
-compel/Makefile: ;
-compel/%: $(compel-deps) $(compel-plugins) .FORCE
- $(Q) $(MAKE) $(build)=compel $@
-
-criu-deps += compel/compel-host-bin
-
-#
-# Make sure the host program is ready after the
-# library and plugins are built.
-compel/compel-host-bin: | compel/$(LIBCOMPEL_A) $(compel-plugins)
-$(COMPEL_BIN): compel/compel-host-bin
-
-#
-# Plugins
-compel/plugins/Makefile: ;
-compel/plugins/%: $(compel-deps) .FORCE
- $(Q) $(MAKE) $(build)=compel/plugins $@
-
-#
-# GNU make 4.x supports targets matching via wide
-# match targeting, where GNU make 3.x series is not,
-# so we have to write them here explicitly.
-compel/plugins/std.lib.a: $(compel-deps) .FORCE
- $(Q) $(MAKE) $(build)=compel/plugins $@
-
-compel/plugins/shmem.lib.a: $(compel-deps) compel/plugins/std.lib.a .FORCE
- $(Q) $(MAKE) $(build)=compel/plugins $@
-
-compel/plugins/fds.lib.a: $(compel-deps) compel/plugins/std.lib.a .FORCE
- $(Q) $(MAKE) $(build)=compel/plugins $@
-
-compel/compel: compel/built-in.o compel/$(LIBCOMPEL_A) | $(compel-deps)
- $(call msg-link, $@)
- $(Q) $(CC) $(CFLAGS) $^ $(WRAPFLAGS) $(LDFLAGS) -rdynamic -o $@
-
-#
-# And compel library.
-LIBCOMPEL_SO_CFLAGS += $(CFLAGS) -rdynamic -Wl,-soname,$(LIBCOMPEL_SO).$(COMPEL_SO_VERSION_MAJOR)
-compel/$(LIBCOMPEL_SO): compel/$(LIBCOMPEL_A)
- $(call msg-link, $@)
- $(Q) $(CC) -shared $(LIBCOMPEL_SO_CFLAGS) -o $@ -Wl,--whole-archive $^ -Wl,--no-whole-archive $(LDFLAGS)
-
-compel-install-targets += compel/$(LIBCOMPEL_SO)
-compel-install-targets += compel/compel
-compel-install-targets += $(compel-plugins)
diff --git a/Makefile.config b/Makefile.config
index f48ea9adc..ac54775fc 100644
--- a/Makefile.config
+++ b/Makefile.config
@@ -1,124 +1,43 @@
-include $(__nmk_dir)utils.mk
-include $(__nmk_dir)msg.mk
+include scripts/utilities.mak
include scripts/feature-tests.mak
-# This is a kludge for $(info ...) to not eat spaces.
-S :=
+CONFIG := include/config.h
-ifeq ($(call try-cc,$(FEATURE_TEST_LIBBSD_DEV),-lbsd),true)
- LIBS_FEATURES += -lbsd
- FEATURE_DEFINES += -DCONFIG_HAS_LIBBSD
-else
- $(info Note: Building without setproctitle() support.)
- $(info $S Install libbsd-devel (RPM) / libbsd-dev (DEB) to fix.)
+ifeq ($(call try-cc,$(LIBBSD_DEV_TEST),-lbsd),y)
+ LIBS += -lbsd
+ DEFINES += -DCONFIG_HAS_LIBBSD
endif
-ifeq ($(call pkg-config-check,libselinux),y)
- LIBS_FEATURES += -lselinux
- FEATURE_DEFINES += -DCONFIG_HAS_SELINUX
+$(CONFIG): scripts/utilities.mak scripts/feature-tests.mak include/config-base.h
+ $(E) " GEN " $@
+ $(Q) @echo '#ifndef __CR_CONFIG_H__' > $@
+ $(Q) @echo '#define __CR_CONFIG_H__' >> $@
+ $(Q) @echo '' >> $@
+ $(Q) @echo '#include "config-base.h"' >> $@
+ $(Q) @echo '' >> $@
+ifeq ($(call try-cc,$(TCP_REPAIR_TEST),),y)
+ $(Q) @echo '#define CONFIG_HAS_TCP_REPAIR' >> $@
endif
-
-ifeq ($(call pkg-config-check,libbpf),y)
- LIBS_FEATURES += -lbpf
- FEATURE_DEFINES += -DCONFIG_HAS_LIBBPF
- export CONFIG_HAS_LIBBPF := y
+ifeq ($(call try-cc,$(PRLIMIT_TEST),),y)
+ $(Q) @echo '#define CONFIG_HAS_PRLIMIT' >> $@
endif
-
-ifeq ($(call pkg-config-check,libdrm),y)
- export CONFIG_AMDGPU := y
- LIBDRM_CFLAGS := $(shell $(PKG_CONFIG) --cflags libdrm)
- ifeq ($(call try-cc,$(FEATURE_TEST_DRM_COLOR_CTM_3X4),,$(LIBDRM_CFLAGS)),true)
- export CONFIG_HAS_DRM_COLOR_CTM_3X4 := y
- FEATURE_DEFINES += -DHAVE_DRM_COLOR_CTM_3X4
- endif
- $(info Note: Building with amdgpu_plugin.)
-else
- $(info Note: Building without amdgpu_plugin.)
- $(info $S Install libdrm-devel (RPM) or libdrm-dev (DEB) to fix.)
+ifeq ($(call try-cc,$(STRLCPY_TEST),$(LIBS)),y)
+ $(Q) @echo '#define CONFIG_HAS_STRLCPY' >> $@
endif
-
-ifeq ($(NO_GNUTLS)x$(call pkg-config-check,gnutls),xy)
- LIBS_FEATURES += -lgnutls
- export CONFIG_GNUTLS := y
- FEATURE_DEFINES += -DCONFIG_GNUTLS
-else
- $(info Note: Building without GnuTLS support.)
- $(info $S Install gnutls-devel (RPM) or gnutls-dev (DEB) to fix.)
+ifeq ($(call try-cc,$(STRLCAT_TEST),$(LIBS)),y)
+ $(Q) @echo '#define CONFIG_HAS_STRLCAT' >> $@
endif
-
-ifeq ($(NO_LZ4)x$(call pkg-config-check,liblz4),xy)
- LIBS_FEATURES += -llz4
- export CONFIG_LZ4 := y
- FEATURE_DEFINES += -DCONFIG_LZ4
-else
- $(info Note: Building without LZ4 compression support.)
+ifeq ($(call try-cc,$(PTRACE_PEEKSIGINFO_TEST),),y)
+ $(Q) @echo '#define CONFIG_HAS_PEEKSIGINFO_ARGS' >> $@
endif
-
-ifeq ($(call pkg-config-check,libnftables),y)
- LIB_NFTABLES := $(shell $(PKG_CONFIG) --libs libnftables)
- ifeq ($(call try-cc,$(FEATURE_TEST_NFTABLES_LIB_API_0),$(LIB_NFTABLES)),true)
- LIBS_FEATURES += $(LIB_NFTABLES)
- FEATURE_DEFINES += -DCONFIG_HAS_NFTABLES_LIB_API_0
- else ifeq ($(call try-cc,$(FEATURE_TEST_NFTABLES_LIB_API_1),$(LIB_NFTABLES)),true)
- LIBS_FEATURES += $(LIB_NFTABLES)
- FEATURE_DEFINES += -DCONFIG_HAS_NFTABLES_LIB_API_1
- else
- $(info Warn: Building without nftables support (incompatible API version).)
- endif
-else
- $(info Warn: Building without nftables support.)
- $(info $S Install nftables-devel (RPM) or libnftables-dev (DEB) to fix.)
+ifeq ($(VDSO),y)
+ $(Q) @echo '#define CONFIG_VDSO' >> $@
endif
-
-export LIBS += $(LIBS_FEATURES)
-
-ifneq ($(PLUGINDIR),)
- FEATURE_DEFINES += -DCR_PLUGIN_DEFAULT="\"$(PLUGINDIR)\""
+ifeq ($(call try-cc,$(SETPROCTITLE_INIT_TEST),-lbsd),y)
+ $(Q) @echo '#define CONFIG_HAS_SETPROCTITLE_INIT' >> $@
endif
+ $(Q) @echo '#endif /* __CR_CONFIG_H__ */' >> $@
-CONFIG_FILE = .config
+config: $(CONFIG)
-$(CONFIG_FILE):
- touch $(CONFIG_FILE)
-
-ifeq ($(ARCH),x86)
-# CONFIG_COMPAT is only for x86 now, no need for compile-test other archs
-ifeq ($(call try-asm,$(FEATURE_TEST_X86_COMPAT)),true)
- export CONFIG_COMPAT := y
- FEATURE_DEFINES += -DCONFIG_COMPAT
-else
- $(info Note: Building without ia32 C/R, missing ia32 support in gcc.)
- $(info $S It may be related to missing gcc-multilib in your)
- $(info $S distribution, or you may have Debian with buggy toolchain.)
- $(info $S See https://github.com/checkpoint-restore/criu/issues/315.)
-endif
-endif
-
-export DEFINES += $(FEATURE_DEFINES)
-export CFLAGS += $(FEATURE_DEFINES)
-
-FEATURES_LIST := TCP_REPAIR PTRACE_PEEKSIGINFO \
- SETPROCTITLE_INIT TCP_REPAIR_WINDOW MEMFD_CREATE \
- OPENAT2 NO_LIBC_RSEQ_DEFS
-
-# $1 - config name
-define gen-feature-test
-ifeq ($$(call try-cc,$$(FEATURE_TEST_$(1)),$$(LIBS_FEATURES),$$(DEFINES)),true)
- $(Q) echo '#define CONFIG_HAS_$(1)' >> $$@
-else
- $(Q) echo '// CONFIG_HAS_$(1) is not set' >> $$@
-endif
-endef
-
-define config-header-rule
-$(CONFIG_HEADER): scripts/feature-tests.mak $(CONFIG_FILE)
- $(call msg-gen, $$@)
- $(Q) echo '#ifndef __CR_CONFIG_H__' > $$@
- $(Q) echo '#define __CR_CONFIG_H__' >> $$@
- $(Q) echo '' >> $$@
-$(call map,gen-feature-test,$(FEATURES_LIST))
- $(Q) cat $(CONFIG_FILE) | sed -n -e '/^[^#]/s/^/#define CONFIG_/p' >> $$@
- $(Q) echo '#endif /* __CR_CONFIG_H__ */' >> $$@
-endef
-
-$(eval $(config-header-rule))
+.PHONY: config
diff --git a/Makefile.crtools b/Makefile.crtools
new file mode 100644
index 000000000..650b9b0c3
--- /dev/null
+++ b/Makefile.crtools
@@ -0,0 +1,87 @@
+obj-y += parasite-syscall.o
+obj-y += mem.o
+obj-y += rst-malloc.o
+obj-y += cr-restore.o
+obj-y += crtools.o
+obj-y += security.o
+obj-y += image.o
+obj-y += image-desc.o
+obj-y += net.o
+obj-y += tun.o
+obj-y += proc_parse.o
+obj-y += sysfs_parse.o
+obj-y += cr-dump.o
+obj-y += cr-show.o
+obj-y += cr-check.o
+obj-y += cr-dedup.o
+obj-y += util.o
+obj-y += bfd.o
+obj-y += action-scripts.o
+obj-y += sysctl.o
+obj-y += ptrace.o
+obj-y += kcmp-ids.o
+obj-y += rbtree.o
+obj-y += log.o
+obj-y += libnetlink.o
+obj-y += sockets.o
+obj-y += sk-inet.o
+obj-y += sk-tcp.o
+obj-y += sk-unix.o
+obj-y += sk-packet.o
+obj-y += sk-netlink.o
+obj-y += sk-queue.o
+obj-y += files.o
+obj-y += files-reg.o
+obj-y += files-ext.o
+obj-y += pipes.o
+obj-y += fifo.o
+obj-y += file-ids.o
+obj-y += namespaces.o
+obj-y += uts_ns.o
+obj-y += ipc_ns.o
+obj-y += netfilter.o
+obj-y += shmem.o
+obj-y += eventfd.o
+obj-y += eventpoll.o
+obj-y += mount.o
+obj-y += fsnotify.o
+obj-y += irmap.o
+obj-y += signalfd.o
+obj-y += pstree.o
+obj-y += protobuf.o
+obj-y += protobuf-desc.o
+obj-y += tty.o
+obj-y += cr-exec.o
+obj-y += file-lock.o
+obj-y += page-pipe.o
+obj-y += page-xfer.o
+obj-y += page-read.o
+obj-y += pagemap-cache.o
+obj-y += kerndat.o
+obj-y += stats.o
+obj-y += cgroup.o
+obj-y += timerfd.o
+obj-y += aio.o
+obj-y += string.o
+obj-y += sigframe.o
+ifeq ($(VDSO),y)
+obj-y += $(ARCH_DIR)/vdso.o
+endif
+obj-y += cr-service.o
+obj-y += sd-daemon.o
+obj-y += plugin.o
+obj-y += cr-errno.o
+
+ifneq ($(MAKECMDGOALS),clean)
+incdeps := y
+endif
+
+PROTOBUF_GEN := scripts/protobuf-gen.sh
+
+protobuf-desc.c: protobuf-desc-gen.h
+
+protobuf-desc-gen.h: $(PROTOBUF_GEN) include/protobuf-desc.h
+ $(E) " GEN " $@
+ $(Q) $(SH) $(obj)/$(PROTOBUF_GEN) > $@
+
+cleanup-y += protobuf-desc-gen.h
diff --git a/Makefile.inc b/Makefile.inc
new file mode 100644
index 000000000..5496f4114
--- /dev/null
+++ b/Makefile.inc
@@ -0,0 +1,30 @@
+# Silent make rules
+
+ifeq ($(strip $(V)),)
+ E = @echo
+ Q = @
+else
+ E = @\#
+ Q =
+endif
+
+export E Q
+
+# Installation paths
+PREFIX := /usr/local
+SBINDIR := $(PREFIX)/sbin
+MANDIR := $(PREFIX)/share/man
+SYSTEMDUNITDIR := $(PREFIX)/lib/systemd/system/
+LOGROTATEDIR := $(PREFIX)/etc/logrotate.d/
+LIBDIR := $(PREFIX)/lib
+# For recent Debian/Ubuntu with multiarch support
+DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture \
+ -qDEB_HOST_MULTIARCH 2>/dev/null)
+ifneq "$(DEB_HOST_MULTIARCH)" ""
+LIBDIR := $(PREFIX)/lib/$(DEB_HOST_MULTIARCH)
+# For most other systems
+else ifeq "$(shell uname -m)" "x86_64"
+LIBDIR := $(PREFIX)/lib64
+endif
+
+INCLUDEDIR := $(PREFIX)/include/criu
diff --git a/Makefile.install b/Makefile.install
deleted file mode 100644
index 70c607ec6..000000000
--- a/Makefile.install
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-# Installation paths.
-PREFIX ?= /usr/local
-BINDIR ?= $(PREFIX)/bin
-SBINDIR ?= $(PREFIX)/sbin
-MANDIR ?= $(PREFIX)/share/man
-INCLUDEDIR ?= $(PREFIX)/include
-LIBEXECDIR ?= $(PREFIX)/libexec
-RUNDIR ?= /run
-PLUGINDIR ?= $(PREFIX)/lib/criu
-
-#
-# For recent Debian/Ubuntu with multiarch support.
-DEB_HOST_MULTIARCH := $(shell dpkg-architecture -qDEB_HOST_MULTIARCH 2>/dev/null)
-ifneq "$(DEB_HOST_MULTIARCH)" ""
- LIBDIR ?= $(PREFIX)/lib/$(DEB_HOST_MULTIARCH)
-else
- #
- # For most other systems
- ifeq "$(shell uname -m)" "x86_64"
- LIBDIR ?= $(PREFIX)/lib64
- endif
-endif
-
-#
-# LIBDIR falls back to the standard path.
-LIBDIR ?= $(PREFIX)/lib
-
-export PREFIX BINDIR SBINDIR MANDIR RUNDIR
-export LIBDIR INCLUDEDIR LIBEXECDIR PLUGINDIR
-
-# Detect externally managed Python environment (PEP 668).
-PYTHON_EXTERNALLY_MANAGED := $(shell $(PYTHON) -c 'import os, sysconfig; print(int(os.path.isfile(os.path.join(sysconfig.get_path("stdlib"), "EXTERNALLY-MANAGED"))))')
-PIP_BREAK_SYSTEM_PACKAGES ?= 0
-
-# If Python environment is externally managed and PIP_BREAK_SYSTEM_PACKAGES is not set, skip pip install.
-SKIP_PIP_INSTALL := 0
-ifeq ($(PYTHON_EXTERNALLY_MANAGED),1)
-ifeq ($(PIP_BREAK_SYSTEM_PACKAGES),0)
-
-SKIP_PIP_INSTALL := 1
-$(info Warn: Externally managed python environment)
-$(info Consider using PIP_BREAK_SYSTEM_PACKAGES=1)
-
-endif
-endif
-
-# Default flags for pip install:
-# --ignore-installed: Overwrite already installed pycriu/crit packages
-# --no-build-isolation: Use current Python environment to build pycriu/crit packages
-# --no-deps: Don't install any dependencies
-# --no-index: Don't use PyPI index to find packages
-# --progress-bar: Cleaner output
-# --upgrade: Treat the install as an upgrade when replacing the installed version
-PIPFLAGS ?= --ignore-installed --no-build-isolation --no-deps --no-index --progress-bar off --upgrade
-
-export SKIP_PIP_INSTALL PIPFLAGS
-
-install-man:
- $(Q) $(MAKE) -C Documentation install
-.PHONY: install-man
-
-install-lib: lib
- $(Q) $(MAKE) $(build)=lib install
-.PHONY: install-lib
-
-install-crit: lib
- $(Q) $(MAKE) $(build)=crit install
-.PHONY: install-crit
-
-install-criu: criu
- $(Q) $(MAKE) $(build)=criu install
-.PHONY: install-criu
-
-install-amdgpu_plugin: amdgpu_plugin
- $(Q) $(MAKE) -C plugins/amdgpu install
-.PHONY: install-amdgpu_plugin
-
-install-cuda_plugin: cuda_plugin
- $(Q) $(MAKE) -C plugins/cuda install
-.PHONY: install-cuda_plugin
-
-install-compel: $(compel-install-targets)
- $(Q) $(MAKE) $(build)=compel install
- $(Q) $(MAKE) $(build)=compel/plugins install
-.PHONY: install-compel
-
-install: install-man install-lib install-crit install-criu install-compel install-amdgpu_plugin install-cuda_plugin ;
-.PHONY: install
-
-uninstall:
- $(Q) $(MAKE) -C Documentation $@
- $(Q) $(MAKE) $(build)=lib $@
- $(Q) $(MAKE) $(build)=crit $@
- $(Q) $(MAKE) $(build)=criu $@
- $(Q) $(MAKE) $(build)=compel $@
- $(Q) $(MAKE) $(build)=compel/plugins $@
- $(Q) $(MAKE) -C plugins/amdgpu $@
- $(Q) $(MAKE) -C plugins/cuda $@
-.PHONY: uninstall
diff --git a/Makefile.versions b/Makefile.versions
deleted file mode 100644
index 3e6c9ed22..000000000
--- a/Makefile.versions
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# CRIU version.
-CRIU_VERSION_MAJOR := 4
-CRIU_VERSION_MINOR := 2
-CRIU_VERSION_SUBLEVEL :=
-CRIU_VERSION_EXTRA :=
-CRIU_VERSION_NAME := CRIUTIBILITY
-CRIU_VERSION := $(CRIU_VERSION_MAJOR)$(if $(CRIU_VERSION_MINOR),.$(CRIU_VERSION_MINOR))$(if $(CRIU_VERSION_SUBLEVEL),.$(CRIU_VERSION_SUBLEVEL))$(if $(CRIU_VERSION_EXTRA),.$(CRIU_VERSION_EXTRA))
-
-export CRIU_VERSION_MAJOR CRIU_VERSION_MINOR CRIU_VERSION_SUBLEVEL
-export CRIU_VERSION_EXTRA CRIU_VERSION_NAME CRIU_VERSION
-
-#
-# C library for CRIU.
-CRIU_SO_VERSION_MAJOR := 2
-CRIU_SO_VERSION_MINOR := 0
-
-export CRIU_SO_VERSION_MAJOR CRIU_SO_VERSION_MINOR
-
-#
-# SOCCR library.
-SOCCR_SO_VERSION_MAJOR := 1
-SOCCR_SO_VERSION_MINOR := 0
-
-export SOCCR_SO_VERSION_MAJOR SOCCR_SO_VERSION_MINOR
-
-COMPEL_SO_VERSION_MAJOR := 1
-COMPEL_SO_VERSION_MINOR := 0
-COMPEL_SO_VERSION_SUBLEVEL := 0
-
-export COMPEL_SO_VERSION_MAJOR COMPEL_SO_VERSION_MINOR COMPEL_SO_VERSION_SUBLEVEL
diff --git a/README b/README
new file mode 100644
index 000000000..e7c87048b
--- /dev/null
+++ b/README
@@ -0,0 +1,16 @@
+criu
+====
+
+An utility to checkpoint/restore tasks. Using this tool, you can
+freeze a running application (or part of it) and checkpoint it to
+a hard drive as a collection of files. You can then use the files
+to restore and run the application from the point it was frozen
+at. The distinctive feature of the CRIU project is that it is
+mainly implemented in user space.
+
+The project home is at http://criu.org
+
+Pages worth starting with are
+* Kernel configuration, compilation, etc: http://criu.org/Installation
+* A simple example of usage: http://criu.org/Simple_loop
+* More sophisticated example with graphical app: http://criu.org/VNC
diff --git a/README.md b/README.md
deleted file mode 100644
index e85d39221..000000000
--- a/README.md
+++ /dev/null
@@ -1,71 +0,0 @@
-[](
- https://github.com/checkpoint-restore/criu/actions/workflows/ci.yml)
-[](
- https://circleci.com/gh/checkpoint-restore/criu)
-
-
-
-## CRIU -- A project to implement checkpoint/restore functionality for Linux
-
-CRIU (stands for Checkpoint and Restore in Userspace) is a utility to checkpoint/restore Linux tasks.
-
-Using this tool, you can freeze a running application (or part of it) and checkpoint
-it to a hard drive as a collection of files. You can then use the files to restore and run the
-application from the point it was frozen at. The distinctive feature of the CRIU
-project is that it is mainly implemented in user space. There are some more projects
-doing C/R for Linux, and so far CRIU [appears to be](https://criu.org/Comparison_to_other_CR_projects)
-the most feature-rich and up-to-date with the kernel.
-
-CRIU project is (almost) the never-ending story, because we have to always keep up with the
-Linux kernel supporting checkpoint and restore for all the features it provides. Thus we're
-looking for contributors of all kinds -- feedback, bug reports, testing, coding, writing, etc.
-Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) if you would like to get involved.
-
-The project [started](https://criu.org/History) as the way to do live migration for OpenVZ
-Linux containers, but later grew to more sophisticated and flexible tool. It is currently
-used by (integrated into) OpenVZ, LXC/LXD, Docker, and other software, project gets tremendous
-help from the community, and its packages are included into many Linux distributions.
-
-The project home is at http://criu.org. This wiki contains all the knowledge base for CRIU we have.
-Pages worth starting with are:
-- [Installation instructions](http://criu.org/Installation)
-- [A simple example of usage](http://criu.org/Simple_loop)
-- [Examples of more advanced usage](https://criu.org/Category:HOWTO)
-- Troubleshooting can be hard, some help can be found [here](https://criu.org/When_C/R_fails), [here](https://criu.org/What_cannot_be_checkpointed) and [here](https://criu.org/index.php?title=FAQ)
-
-### Checkpoint and restore of simple loop process
-
-
-## Advanced features
-
-As main usage for CRIU is live migration, there's a library for it called P.Haul. Also the
-project exposes two cool core features as standalone libraries. These are libcompel for parasite code
-injection and libsoccr for TCP connections checkpoint-restore.
-
-### Live migration
-
-True [live migration](https://criu.org/Live_migration) using CRIU is possible, but doing
-all the steps by hands might be complicated. The [phaul sub-project](https://criu.org/P.Haul)
-provides a Go library that encapsulates most of the complexity. This library and the Go bindings
-for CRIU are stored in the [go-criu](https://github.com/checkpoint-restore/go-criu) repository.
-
-
-### Parasite code injection
-
-In order to get state of the running process CRIU needs to make this process execute
-some code, that would fetch the required information. To make this happen without
-killing the application itself, CRIU uses the [parasite code injection](https://criu.org/Parasite_code)
-technique, which is also available as a standalone library called [libcompel](https://criu.org/Compel).
-
-### TCP sockets checkpoint-restore
-
-One of the CRIU features is the ability to save and restore state of a TCP socket
-without breaking the connection. This functionality is considered to be useful by
-itself, and we have it available as the [libsoccr library](https://criu.org/Libsoccr).
-
-## Licence
-
-The project is licensed under GPLv2 (though files sitting in the lib/ directory are LGPLv2.1).
-
-All files in the images/ directory are licensed under the Expat license (so-called MIT).
-See the images/LICENSE file.
diff --git a/action-scripts.c b/action-scripts.c
new file mode 100644
index 000000000..4c39a259a
--- /dev/null
+++ b/action-scripts.c
@@ -0,0 +1,72 @@
+#include
+#include
+#include
+#include
+
+#include "cr_options.h"
+#include "list.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "servicefd.h"
+#include "cr-service.h"
+#include "action-scripts.h"
+
+static const char *action_names[ACT_MAX] = {
+ [ ACT_POST_DUMP ] = "post-dump",
+ [ ACT_POST_RESTORE ] = "post-restore",
+ [ ACT_NET_LOCK ] = "network-lock",
+ [ ACT_NET_UNLOCK ] = "network-unlock",
+ [ ACT_SETUP_NS ] = "setup-namespaces",
+};
+
+int run_scripts(enum script_actions act)
+{
+ struct script *script;
+ int ret = 0;
+ char image_dir[PATH_MAX];
+ const char *action = action_names[act];
+
+ pr_debug("Running %s scripts\n", action);
+
+ if (unlikely(list_empty(&opts.scripts)))
+ return 0;
+
+ if (setenv("CRTOOLS_SCRIPT_ACTION", action, 1)) {
+ pr_perror("Can't set CRTOOLS_SCRIPT_ACTION=%s", action);
+ return -1;
+ }
+
+ sprintf(image_dir, "/proc/%ld/fd/%d", (long) getpid(), get_service_fd(IMG_FD_OFF));
+ if (setenv("CRTOOLS_IMAGE_DIR", image_dir, 1)) {
+ pr_perror("Can't set CRTOOLS_IMAGE_DIR=%s", image_dir);
+ return -1;
+ }
+
+ list_for_each_entry(script, &opts.scripts, node) {
+ if (script->path == SCRIPT_RPC_NOTIFY) {
+ pr_debug("\tRPC\n");
+ ret |= send_criu_rpc_script(act, (char *)action, script->arg);
+ } else {
+ pr_debug("\t[%s]\n", script->path);
+ ret |= system(script->path);
+ }
+ }
+
+ unsetenv("CRTOOLS_SCRIPT_ACTION");
+ return ret;
+}
+
+int add_script(char *path, int arg)
+{
+ struct script *script;
+
+ script = xmalloc(sizeof(struct script));
+ if (script == NULL)
+ return 1;
+
+ script->path = path;
+ script->arg = arg;
+ list_add(&script->node, &opts.scripts);
+
+ return 0;
+}
diff --git a/aio.c b/aio.c
new file mode 100644
index 000000000..9965efd8c
--- /dev/null
+++ b/aio.c
@@ -0,0 +1,120 @@
+#include
+#include
+#include
+#include "vma.h"
+#include "xmalloc.h"
+#include "aio.h"
+#include "parasite.h"
+#include "parasite-syscall.h"
+#include "protobuf/mm.pb-c.h"
+
+int dump_aio_ring(MmEntry *mme, struct vma_area *vma)
+{
+ int nr = mme->n_aios;
+ AioRingEntry *re;
+
+ pr_info("Dumping AIO ring @%"PRIx64", %u reqs\n",
+ vma->e->start, vma->aio_nr_req);
+
+ mme->aios = xrealloc(mme->aios, (nr + 1) * sizeof(re));
+ if (!mme->aios)
+ return -1;
+
+ re = xmalloc(sizeof(*re));
+ if (!re)
+ return -1;
+
+ aio_ring_entry__init(re);
+ re->id = vma->e->start;
+ re->nr_req = vma->aio_nr_req;
+ re->ring_len = vma->e->end - vma->e->start;
+ mme->aios[nr] = re;
+ mme->n_aios = nr + 1;
+ return 0;
+}
+
+void free_aios(MmEntry *mme)
+{
+ int i;
+
+ if (mme->aios) {
+ for (i = 0; i < mme->n_aios; i++)
+ xfree(mme->aios[i]);
+ xfree(mme->aios);
+ }
+}
+
+static unsigned int aio_estimate_nr_reqs(unsigned int k_max_reqs)
+{
+ /*
+ * Kernel does
+ *
+ * nr_reqs = max(nr_reqs, nr_cpus * 4)
+ * nr_reqs *= 2
+ * nr_reqs += 2
+ * ring = roundup(sizeof(head) + nr_reqs * sizeof(req))
+ * nr_reqs = (ring - sizeof(head)) / sizeof(req)
+ *
+ * And the k_max_reqs here is the resulting value.
+ *
+ * We need to get the initial nr_reqs that would grow
+ * up back to the k_max_reqs.
+ */
+
+ return (k_max_reqs - 2) / 2;
+}
+
+unsigned long aio_rings_args_size(struct vm_area_list *vmas)
+{
+ return sizeof(struct parasite_check_aios_args) +
+ vmas->nr_aios * sizeof(struct parasite_aio);
+}
+
+int parasite_check_aios(struct parasite_ctl *ctl, struct vm_area_list *vmas)
+{
+ struct vma_area *vma;
+ struct parasite_check_aios_args *aa;
+ struct parasite_aio *pa;
+ int i;
+
+ if (!vmas->nr_aios)
+ return 0;
+
+ pr_info("Checking AIO rings\n");
+
+ /*
+ * Go to parasite and
+ * a) check that no requests are currently pengind
+ * b) get the maximum number of requests kernel handles
+ * to estimate what was the user request on ring
+ * creation.
+ */
+
+ aa = parasite_args_s(ctl, aio_rings_args_size(vmas));
+ pa = &aa->ring[0];
+ list_for_each_entry(vma, &vmas->h, list) {
+ if (!vma_area_is(vma, VMA_AREA_AIORING))
+ continue;
+
+ pr_debug(" `- Ring #%ld @%"PRIx64"\n",
+ (long)(pa - &aa->ring[0]), vma->e->start);
+ pa->ctx = vma->e->start;
+ pa->max_reqs = 0;
+ pa->vma_nr_reqs = &vma->aio_nr_req;
+ pa++;
+ }
+ aa->nr_rings = vmas->nr_aios;
+
+ if (parasite_execute_daemon(PARASITE_CMD_CHECK_AIOS, ctl))
+ return -1;
+
+ pa = &aa->ring[0];
+ for (i = 0; i < vmas->nr_aios; i++) {
+ pa = &aa->ring[i];
+ *pa->vma_nr_reqs = aio_estimate_nr_reqs(pa->max_reqs);
+ pr_debug(" `- Ring #%d has %u reqs, estimated to %u\n", i,
+ pa->max_reqs, *pa->vma_nr_reqs);
+ }
+
+ return 0;
+}
diff --git a/arch/aarch64/Makefile b/arch/aarch64/Makefile
new file mode 100644
index 000000000..200d37c72
--- /dev/null
+++ b/arch/aarch64/Makefile
@@ -0,0 +1,59 @@
+targets += syscalls
+targets += crtools
+
+SYS-ASM := syscalls.S
+
+syscalls-asm-y += $(SYS-ASM:.S=).o
+crtools-obj-y += crtools.o
+crtools-obj-y += cpu.o
+
+SYS-DEF := ../arm/syscall.def
+SYS-ASM-COMMON := syscall-common.S
+SYS-TYPES := include/syscall-types.h
+
+SYS-CODES := include/syscall-codes.h
+SYS-PROTO := include/syscall.h
+
+SYS-GEN := ../scripts/arm/gen-syscalls.pl
+SYS-GEN-TBL := ../scripts/arm/gen-sys-exec-tbl.pl
+
+SYS-EXEC-TBL := sys-exec-tbl.c
+
+syscalls-asm-y-asmflags += -fpie -Wstrict-prototypes -Wa,--noexecstack
+syscalls-asm-y-asmflags += -nostdlib -fomit-frame-pointer -I$(obj)
+ASMFLAGS += -D__ASSEMBLY__
+
+ARCH_BITS := 64
+
+$(obj)/$(SYS-ASM): $(obj)/$(SYS-GEN) $(obj)/$(SYS-DEF) $(obj)/$(SYS-ASM-COMMON) $(SYS-TYPES)
+ $(E) " GEN " $@
+ $(Q) perl \
+ $(obj)/$(SYS-GEN) \
+ $(obj)/$(SYS-DEF) \
+ $(SYS-CODES) \
+ $(SYS-PROTO) \
+ $(obj)/$(SYS-ASM) \
+ $(SYS-ASM-COMMON) \
+ $(SYS-TYPES) \
+ $(ARCH_BITS)
+
+$(obj)/syscalls.o: $(obj)/$(SYS-ASM)
+
+$(obj)/$(SYS-EXEC-TBL): $(obj)/$(SYS-GEN-TBL) $(obj)/$(SYS-DEF)
+ $(E) " GEN " $@
+ $(Q) perl \
+ $(obj)/$(SYS-GEN-TBL) \
+ $(obj)/$(SYS-DEF) \
+ $(obj)/$(SYS-EXEC-TBL) \
+ $(ARCH_BITS)
+
+_all += $(obj)/$(SYS-EXEC-TBL)
+
+cleanup-y += $(obj)/$(SYS-EXEC-TBL) $(obj)/$(SYS-ASM)
+cleanup-y += $(SYS-CODES)
+cleanup-y += $(SYS-PROTO)
+
+ifneq ($(MAKECMDGOALS),clean)
+deps-after := $(obj)/$(SYS-ASM)
+incdeps := y
+endif
diff --git a/arch/aarch64/cpu.c b/arch/aarch64/cpu.c
new file mode 100644
index 000000000..040fe14fc
--- /dev/null
+++ b/arch/aarch64/cpu.c
@@ -0,0 +1,45 @@
+#undef LOG_PREFIX
+#define LOG_PREFIX "cpu: "
+
+#include
+#include "cpu.h"
+
+bool cpu_has_feature(unsigned int feature)
+{
+ return false;
+}
+
+int cpu_init(void)
+{
+ return 0;
+}
+
+int cpu_dump_cpuinfo(void)
+{
+ return 0;
+}
+
+int cpu_validate_cpuinfo(void)
+{
+ return 0;
+}
+
+int cpu_dump_cpuinfo_single(void)
+{
+ return -ENOTSUP;
+}
+
+int cpu_validate_image_cpuinfo_single(void)
+{
+ return -ENOTSUP;
+}
+
+int cpuinfo_dump(void)
+{
+ return -ENOTSUP;
+}
+
+int cpuinfo_check(void)
+{
+ return -ENOTSUP;
+}
diff --git a/arch/aarch64/crtools.c b/arch/aarch64/crtools.c
new file mode 100644
index 000000000..13668cdb5
--- /dev/null
+++ b/arch/aarch64/crtools.c
@@ -0,0 +1,235 @@
+#include
+#include
+
+#include
+
+#include "asm/types.h"
+#include "asm/restorer.h"
+#include "compiler.h"
+#include "ptrace.h"
+#include "asm/processor-flags.h"
+#include "protobuf.h"
+#include "protobuf/core.pb-c.h"
+#include "protobuf/creds.pb-c.h"
+#include "parasite-syscall.h"
+#include "syscall.h"
+#include "log.h"
+#include "util.h"
+#include "cpu.h"
+#include "parasite-syscall.h"
+#include "restorer.h"
+
+
+/*
+ * Injected syscall instruction
+ */
+const char code_syscall[] = {
+ 0x01, 0x00, 0x00, 0xd4, /* SVC #0 */
+ 0x00, 0x00, 0x20, 0xd4 /* BRK #0 */
+};
+
+const int code_syscall_size = round_up(sizeof(code_syscall), sizeof(long));
+
+static inline void __check_code_syscall(void)
+{
+ BUILD_BUG_ON(sizeof(code_syscall) != BUILTIN_SYSCALL_SIZE);
+ BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
+}
+
+
+void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *regs)
+{
+ regs->pc = new_ip;
+ if (stack)
+ regs->sp = (unsigned long)stack;
+}
+
+bool arch_can_dump_task(pid_t pid)
+{
+ /*
+ * TODO: Add proper check here
+ */
+ return true;
+}
+
+int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
+ unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5,
+ unsigned long arg6)
+{
+ user_regs_struct_t regs = ctl->orig.regs;
+ int err;
+
+ regs.regs[8] = (unsigned long)nr;
+ regs.regs[0] = arg1;
+ regs.regs[1] = arg2;
+ regs.regs[2] = arg3;
+ regs.regs[3] = arg4;
+ regs.regs[4] = arg5;
+ regs.regs[5] = arg6;
+ regs.regs[6] = 0;
+ regs.regs[7] = 0;
+
+ err = __parasite_execute_syscall(ctl, ®s);
+
+ *ret = regs.regs[0];
+ return err;
+}
+
+
+#define assign_reg(dst, src, e) dst->e = (__typeof__(dst->e))(src).e
+
+int get_task_regs(pid_t pid, user_regs_struct_t regs, CoreEntry *core)
+{
+ struct iovec iov;
+ struct user_fpsimd_state fpsimd;
+ int i, ret;
+
+ pr_info("Dumping GP/FPU registers for %d\n", pid);
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(user_regs_struct_t);
+ if ((ret = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))) {
+ pr_err("Failed to obtain CPU registers for %d!", pid);
+ goto err;
+ }
+
+ iov.iov_base = &fpsimd;
+ iov.iov_len = sizeof(fpsimd);
+ if ((ret = ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov))) {
+ pr_err("Failed to obtain FPU registers for %d!", pid);
+ goto err;
+ }
+
+
+ // Save the Aarch64 CPU state
+ for (i = 0; i < 31; ++i)
+ assign_reg(core->ti_aarch64->gpregs, regs, regs[i]);
+ assign_reg(core->ti_aarch64->gpregs, regs, sp);
+ assign_reg(core->ti_aarch64->gpregs, regs, pc);
+ assign_reg(core->ti_aarch64->gpregs, regs, pstate);
+
+
+ // Save the FP/SIMD state
+ for (i = 0; i < 32; ++i)
+ {
+ core->ti_aarch64->fpsimd->vregs[2*i] = fpsimd.vregs[i];
+ core->ti_aarch64->fpsimd->vregs[2*i + 1] = fpsimd.vregs[i] >> 64;
+ }
+ assign_reg(core->ti_aarch64->fpsimd, fpsimd, fpsr);
+ assign_reg(core->ti_aarch64->fpsimd, fpsimd, fpcr);
+
+ ret = 0;
+
+err:
+ return ret;
+}
+
+int arch_alloc_thread_info(CoreEntry *core)
+{
+ ThreadInfoAarch64 *ti_aarch64;
+ UserAarch64RegsEntry *gpregs;
+ UserAarch64FpsimdContextEntry *fpsimd;
+
+ ti_aarch64 = xmalloc(sizeof(*ti_aarch64));
+ if (!ti_aarch64)
+ goto err;
+ thread_info_aarch64__init(ti_aarch64);
+ core->ti_aarch64 = ti_aarch64;
+
+ gpregs = xmalloc(sizeof(*gpregs));
+ if (!gpregs)
+ goto err;
+ user_aarch64_regs_entry__init(gpregs);
+
+ gpregs->regs = xmalloc(31*sizeof(uint64_t));
+ if (!gpregs->regs)
+ goto err;
+ gpregs->n_regs = 31;
+
+ ti_aarch64->gpregs = gpregs;
+
+ fpsimd = xmalloc(sizeof(*fpsimd));
+ if (!fpsimd)
+ goto err;
+ user_aarch64_fpsimd_context_entry__init(fpsimd);
+ ti_aarch64->fpsimd = fpsimd;
+ fpsimd->vregs = xmalloc(64*sizeof(fpsimd->vregs[0]));
+ fpsimd->n_vregs = 64;
+ if (!fpsimd->vregs)
+ goto err;
+
+ return 0;
+err:
+ return -1;
+}
+
+void arch_free_thread_info(CoreEntry *core)
+{
+ if (CORE_THREAD_ARCH_INFO(core)) {
+ if (CORE_THREAD_ARCH_INFO(core)->fpsimd) {
+ xfree(CORE_THREAD_ARCH_INFO(core)->fpsimd->vregs);
+ xfree(CORE_THREAD_ARCH_INFO(core)->fpsimd);
+ }
+ xfree(CORE_THREAD_ARCH_INFO(core)->gpregs->regs);
+ xfree(CORE_THREAD_ARCH_INFO(core)->gpregs);
+ xfree(CORE_THREAD_ARCH_INFO(core));
+ CORE_THREAD_ARCH_INFO(core) = NULL;
+ }
+}
+
+int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
+{
+ int i;
+ struct fpsimd_context *fpsimd = &RT_SIGFRAME_FPU(sigframe);
+
+ if (core->ti_aarch64->fpsimd->n_vregs != 64)
+ return 1;
+
+ for (i = 0; i < 32; ++i)
+ fpsimd->vregs[i] = (__uint128_t)core->ti_aarch64->fpsimd->vregs[2*i] |
+ ((__uint128_t)core->ti_aarch64->fpsimd->vregs[2*i + 1] << 64);
+ assign_reg(fpsimd, *core->ti_aarch64->fpsimd, fpsr);
+ assign_reg(fpsimd, *core->ti_aarch64->fpsimd, fpcr);
+
+ fpsimd->head.magic = FPSIMD_MAGIC;
+ fpsimd->head.size = sizeof(*fpsimd);
+
+ return 0;
+}
+
+void *mmap_seized(
+ struct parasite_ctl *ctl,
+ void *addr, size_t length, int prot,
+ int flags, int fd, off_t offset)
+{
+ unsigned long map;
+ int err;
+
+ err = syscall_seized(ctl, __NR_mmap, &map,
+ (unsigned long)addr, length, prot, flags, fd, offset);
+ if (err < 0 || (long)map < 0)
+ map = 0;
+
+ return (void *)map;
+}
+
+int restore_gpregs(struct rt_sigframe *f, UserRegsEntry *r)
+{
+#define CPREG1(d) f->uc.uc_mcontext.d = r->d
+
+ int i;
+
+ for (i = 0; i < 31; ++i)
+ CPREG1(regs[i]);
+ CPREG1(sp);
+ CPREG1(pc);
+ CPREG1(pstate);
+
+#undef CPREG1
+
+ return 0;
+}
diff --git a/arch/aarch64/include/asm/atomic.h b/arch/aarch64/include/asm/atomic.h
new file mode 100644
index 000000000..0e1c04f5a
--- /dev/null
+++ b/arch/aarch64/include/asm/atomic.h
@@ -0,0 +1,98 @@
+#ifndef __CR_ATOMIC_H__
+#define __CR_ATOMIC_H__
+
+typedef struct {
+ int counter;
+} atomic_t;
+
+
+/* Copied from the Linux header arch/arm/include/asm/barrier.h */
+
+#define smp_mb() asm volatile("dmb ish" : : : "memory")
+
+
+/* Copied from the Linux kernel header arch/arm64/include/asm/atomic.h */
+
+static inline int atomic_read(const atomic_t *v)
+{
+ return (*(volatile int *)&(v)->counter);
+}
+
+static inline void atomic_set(atomic_t *v, int i)
+{
+ v->counter = i;
+}
+
+#define atomic_get atomic_read
+
+
+static inline int atomic_add_return(int i, atomic_t *v)
+{
+ unsigned long tmp;
+ int result;
+
+ asm volatile(
+"1: ldxr %w0, %2\n"
+" add %w0, %w0, %w3\n"
+" stlxr %w1, %w0, %2\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+Q" (v->counter)
+ : "Ir" (i)
+ : "cc", "memory");
+
+ smp_mb();
+ return result;
+}
+
+static inline int atomic_sub_return(int i, atomic_t *v)
+{
+ unsigned long tmp;
+ int result;
+
+ asm volatile(
+"1: ldxr %w0, %2\n"
+" sub %w0, %w0, %w3\n"
+" stlxr %w1, %w0, %2\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+Q" (v->counter)
+ : "Ir" (i)
+ : "cc", "memory");
+
+ smp_mb();
+ return result;
+}
+
+static inline int atomic_inc(atomic_t *v) { return atomic_add_return(1, v) - 1; }
+
+static inline int atomic_add(int val, atomic_t *v) { return atomic_add_return(val, v) - val; }
+
+static inline int atomic_dec(atomic_t *v) { return atomic_sub_return(1, v) + 1; }
+
+/* true if the result is 0, or false for all other cases. */
+#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
+
+#define atomic_inc_return(v) (atomic_add_return(1, v))
+
+static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
+{
+ unsigned long tmp;
+ int oldval;
+
+ smp_mb();
+
+ asm volatile("// atomic_cmpxchg\n"
+"1: ldxr %w1, %2\n"
+" cmp %w1, %w3\n"
+" b.ne 2f\n"
+" stxr %w0, %w4, %2\n"
+" cbnz %w0, 1b\n"
+"2:"
+ : "=&r" (tmp), "=&r" (oldval), "+Q" (ptr->counter)
+ : "Ir" (old), "r" (new)
+ : "cc");
+
+ smp_mb();
+ return oldval;
+}
+
+#endif /* __CR_ATOMIC_H__ */
diff --git a/arch/aarch64/include/asm/bitops.h b/arch/aarch64/include/asm/bitops.h
new file mode 100644
index 000000000..5a750447f
--- /dev/null
+++ b/arch/aarch64/include/asm/bitops.h
@@ -0,0 +1,7 @@
+#ifndef __CR_ASM_BITOPS_H__
+#define __CR_ASM_BITOPS_H__
+
+#include "compiler.h"
+#include "asm-generic/bitops.h"
+
+#endif /* __CR_ASM_BITOPS_H__ */
diff --git a/include/common/arch/aarch64/asm/bitsperlong.h b/arch/aarch64/include/asm/bitsperlong.h
similarity index 100%
rename from include/common/arch/aarch64/asm/bitsperlong.h
rename to arch/aarch64/include/asm/bitsperlong.h
diff --git a/arch/aarch64/include/asm/cpu.h b/arch/aarch64/include/asm/cpu.h
new file mode 100644
index 000000000..59118c211
--- /dev/null
+++ b/arch/aarch64/include/asm/cpu.h
@@ -0,0 +1 @@
+#include
diff --git a/arch/aarch64/include/asm/dump.h b/arch/aarch64/include/asm/dump.h
new file mode 100644
index 000000000..671c424da
--- /dev/null
+++ b/arch/aarch64/include/asm/dump.h
@@ -0,0 +1,14 @@
+#ifndef __CR_ASM_DUMP_H__
+#define __CR_ASM_DUMP_H__
+
+extern int get_task_regs(pid_t pid, user_regs_struct_t regs, CoreEntry *core);
+extern int arch_alloc_thread_info(CoreEntry *core);
+extern void arch_free_thread_info(CoreEntry *core);
+
+
+static inline void core_put_tls(CoreEntry *core, tls_t tls)
+{
+ core->ti_aarch64->tls = tls;
+}
+
+#endif
diff --git a/compel/arch/aarch64/src/lib/include/uapi/asm/fpu.h b/arch/aarch64/include/asm/fpu.h
similarity index 100%
rename from compel/arch/aarch64/src/lib/include/uapi/asm/fpu.h
rename to arch/aarch64/include/asm/fpu.h
diff --git a/criu/arch/aarch64/include/asm/int.h b/arch/aarch64/include/asm/int.h
similarity index 100%
rename from criu/arch/aarch64/include/asm/int.h
rename to arch/aarch64/include/asm/int.h
diff --git a/arch/aarch64/include/asm/linkage.h b/arch/aarch64/include/asm/linkage.h
new file mode 100644
index 000000000..738064233
--- /dev/null
+++ b/arch/aarch64/include/asm/linkage.h
@@ -0,0 +1,24 @@
+#ifndef __CR_LINKAGE_H__
+#define __CR_LINKAGE_H__
+
+#ifdef __ASSEMBLY__
+
+#define __ALIGN .align 4, 0x00
+#define __ALIGN_STR ".align 4, 0x00"
+
+#define GLOBAL(name) \
+ .globl name; \
+ name:
+
+#define ENTRY(name) \
+ .globl name; \
+ .type name, #function; \
+ __ALIGN; \
+ name:
+
+#define END(sym) \
+ .size sym, . - sym
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __CR_LINKAGE_H__ */
diff --git a/arch/aarch64/include/asm/parasite-syscall.h b/arch/aarch64/include/asm/parasite-syscall.h
new file mode 100644
index 000000000..0c07121da
--- /dev/null
+++ b/arch/aarch64/include/asm/parasite-syscall.h
@@ -0,0 +1,18 @@
+#ifndef __CR_ASM_PARASITE_SYSCALL_H__
+#define __CR_ASM_PARASITE_SYSCALL_H__
+
+struct parasite_ctl;
+
+#define ARCH_SI_TRAP TRAP_BRKPT
+
+
+extern const char code_syscall[];
+extern const int code_syscall_size;
+
+void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *regs);
+
+void *mmap_seized(struct parasite_ctl *ctl,
+ void *addr, size_t length, int prot,
+ int flags, int fd, off_t offset);
+
+#endif
diff --git a/arch/aarch64/include/asm/parasite.h b/arch/aarch64/include/asm/parasite.h
new file mode 100644
index 000000000..2a1e1c12e
--- /dev/null
+++ b/arch/aarch64/include/asm/parasite.h
@@ -0,0 +1,11 @@
+#ifndef __ASM_PARASITE_H__
+#define __ASM_PARASITE_H__
+
+static inline void arch_get_tls(tls_t *ptls)
+{
+ tls_t tls;
+ asm("mrs %0, tpidr_el0" : "=r" (tls));
+ *ptls = tls;
+}
+
+#endif
diff --git a/arch/aarch64/include/asm/processor-flags.h b/arch/aarch64/include/asm/processor-flags.h
new file mode 100644
index 000000000..c1888af36
--- /dev/null
+++ b/arch/aarch64/include/asm/processor-flags.h
@@ -0,0 +1,4 @@
+#ifndef __CR_PROCESSOR_FLAGS_H__
+#define __CR_PROCESSOR_FLAGS_H__
+
+#endif
diff --git a/arch/aarch64/include/asm/restore.h b/arch/aarch64/include/asm/restore.h
new file mode 100644
index 000000000..69404b0e8
--- /dev/null
+++ b/arch/aarch64/include/asm/restore.h
@@ -0,0 +1,28 @@
+#ifndef __CR_ASM_RESTORE_H__
+#define __CR_ASM_RESTORE_H__
+
+#include "asm/restorer.h"
+
+#include "protobuf/core.pb-c.h"
+
+#define JUMP_TO_RESTORER_BLOB(new_sp, restore_task_exec_start, \
+ task_args) \
+ asm volatile( \
+ "and sp, %0, #~15 \n" \
+ "mov x0, %2 \n" \
+ "br %1 \n" \
+ : \
+ : "r"(new_sp), \
+ "r"(restore_task_exec_start), \
+ "r"(task_args) \
+ : "sp", "x0", "memory")
+
+static inline void core_get_tls(CoreEntry *pcore, tls_t *ptls)
+{
+ *ptls = pcore->ti_aarch64->tls;
+}
+
+
+int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core);
+
+#endif
diff --git a/arch/aarch64/include/asm/restorer.h b/arch/aarch64/include/asm/restorer.h
new file mode 100644
index 000000000..583f9583b
--- /dev/null
+++ b/arch/aarch64/include/asm/restorer.h
@@ -0,0 +1,121 @@
+#ifndef __CR_ASM_RESTORER_H__
+#define __CR_ASM_RESTORER_H__
+
+#include
+#include
+
+#include "asm/types.h"
+#include "protobuf/core.pb-c.h"
+
+/* Copied from the kernel header arch/arm64/include/uapi/asm/sigcontext.h */
+
+#define FPSIMD_MAGIC 0x46508001
+
+typedef struct fpsimd_context fpu_state_t;
+
+
+struct aux_context {
+ struct fpsimd_context fpsimd;
+ /* additional context to be added before "end" */
+ struct _aarch64_ctx end;
+};
+
+
+// XXX: the idetifier rt_sigcontext is expected to be struct by the CRIU code
+#define rt_sigcontext sigcontext
+
+
+#include "sigframe.h"
+
+
+/* Copied from the kernel source arch/arm64/kernel/signal.c */
+
+struct rt_sigframe {
+ siginfo_t info;
+ struct ucontext uc;
+ u64 fp;
+ u64 lr;
+};
+
+
+#define ARCH_RT_SIGRETURN(new_sp) \
+ asm volatile( \
+ "mov sp, %0 \n" \
+ "mov x8, #"__stringify(__NR_rt_sigreturn)" \n" \
+ "svc #0 \n" \
+ : \
+ : "r"(new_sp) \
+ : "sp", "x8", "memory")
+
+#define RUN_CLONE_RESTORE_FN(ret, clone_flags, new_sp, parent_tid, \
+ thread_args, clone_restore_fn) \
+ asm volatile( \
+ "clone_emul: \n" \
+ "ldr x1, %2 \n" \
+ "and x1, x1, #~15 \n" \
+ "sub x1, x1, #16 \n" \
+ "stp %5, %6, [x1] \n" \
+ "mov x0, %1 \n" \
+ "mov x2, %3 \n" \
+ "mov x3, %4 \n" \
+ "mov x8, #"__stringify(__NR_clone)" \n" \
+ "svc #0 \n" \
+ \
+ "cbz x0, thread_run \n" \
+ \
+ "mov %0, x0 \n" \
+ "b clone_end \n" \
+ \
+ "thread_run: \n" \
+ "ldp x1, x0, [sp] \n" \
+ "br x1 \n" \
+ \
+ "clone_end: \n" \
+ : "=r"(ret) \
+ : "r"(clone_flags), \
+ "m"(new_sp), \
+ "r"(&parent_tid), \
+ "r"(&thread_args[i].pid), \
+ "r"(clone_restore_fn), \
+ "r"(&thread_args[i]) \
+ : "x0", "x1", "x2", "x3", "x8", "memory")
+
+#define ARCH_FAIL_CORE_RESTORE \
+ asm volatile( \
+ "mov sp, %0 \n" \
+ "mov x0, #0 \n" \
+ "b x0 \n" \
+ : \
+ : "r"(ret) \
+ : "sp", "x0", "memory")
+
+
+#define RT_SIGFRAME_UC(rt_sigframe) rt_sigframe->uc
+#define RT_SIGFRAME_REGIP(rt_sigframe) ((long unsigned int)(rt_sigframe)->uc.uc_mcontext.pc)
+#define RT_SIGFRAME_HAS_FPU(rt_sigframe) (1)
+#define RT_SIGFRAME_FPU(rt_sigframe) ((struct aux_context*)&(rt_sigframe)->uc.uc_mcontext.__reserved)->fpsimd
+
+#define SIGFRAME_OFFSET 0
+
+
+int restore_gpregs(struct rt_sigframe *f, UserAarch64RegsEntry *r);
+int restore_nonsigframe_gpregs(UserAarch64RegsEntry *r);
+
+static inline int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe, fpu_state_t *fpu_state) { return 0; }
+
+static inline void restore_tls(tls_t *ptls)
+{
+ asm("msr tpidr_el0, %0" : : "r" (*ptls));
+}
+
+static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
+{
+ return 0;
+}
+
+static inline int ptrace_flush_breakpoints(pid_t pid)
+{
+ return 0;
+}
+
+#endif
diff --git a/arch/aarch64/include/asm/string.h b/arch/aarch64/include/asm/string.h
new file mode 100644
index 000000000..2c3a34fbb
--- /dev/null
+++ b/arch/aarch64/include/asm/string.h
@@ -0,0 +1,7 @@
+#ifndef __CR_ASM_STRING_H__
+#define __CR_ASM_STRING_H__
+
+#include "compiler.h"
+#include "asm-generic/string.h"
+
+#endif /* __CR_ASM_STRING_H__ */
diff --git a/compel/arch/aarch64/plugins/std/syscalls/syscall-aux.S b/arch/aarch64/include/asm/syscall-aux.S
similarity index 100%
rename from compel/arch/aarch64/plugins/std/syscalls/syscall-aux.S
rename to arch/aarch64/include/asm/syscall-aux.S
diff --git a/arch/aarch64/include/asm/syscall-aux.h b/arch/aarch64/include/asm/syscall-aux.h
new file mode 100644
index 000000000..814c7a9dd
--- /dev/null
+++ b/arch/aarch64/include/asm/syscall-aux.h
@@ -0,0 +1 @@
+#define __NR_openat 56
diff --git a/arch/aarch64/include/asm/types.h b/arch/aarch64/include/asm/types.h
new file mode 100644
index 000000000..8dd336e83
--- /dev/null
+++ b/arch/aarch64/include/asm/types.h
@@ -0,0 +1,84 @@
+#ifndef __CR_ASM_TYPES_H__
+#define __CR_ASM_TYPES_H__
+
+#include
+#include
+#include "protobuf/core.pb-c.h"
+
+#include "asm-generic/page.h"
+#include "asm/bitops.h"
+#include "asm/int.h"
+
+
+#define SIGMAX 64
+#define SIGMAX_OLD 31
+
+typedef void rt_signalfn_t(int, siginfo_t *, void *);
+typedef rt_signalfn_t *rt_sighandler_t;
+
+typedef void rt_restorefn_t(void);
+typedef rt_restorefn_t *rt_sigrestore_t;
+
+#define _KNSIG 64
+#define _NSIG_BPW 64
+
+#define _KNSIG_WORDS (_KNSIG / _NSIG_BPW)
+
+typedef struct {
+ unsigned long sig[_KNSIG_WORDS];
+} k_rtsigset_t;
+
+static inline void ksigfillset(k_rtsigset_t *set)
+{
+ int i;
+ for (i = 0; i < _KNSIG_WORDS; i++)
+ set->sig[i] = (unsigned long)-1;
+}
+
+#define SA_RESTORER 0x00000000
+
+typedef struct {
+ rt_sighandler_t rt_sa_handler;
+ unsigned long rt_sa_flags;
+ rt_sigrestore_t rt_sa_restorer;
+ k_rtsigset_t rt_sa_mask;
+} rt_sigaction_t;
+
+/*
+ * Copied from the Linux kernel header arch/arm64/include/uapi/asm/ptrace.h
+ *
+ * A thread ARM CPU context
+ */
+
+typedef struct user_pt_regs user_regs_struct_t;
+
+
+#define ASSIGN_TYPED(a, b) do { a = (typeof(a))b; } while (0)
+#define ASSIGN_MEMBER(a,b,m) do { ASSIGN_TYPED((a)->m, (b)->m); } while (0)
+
+#define REG_RES(regs) ((u64)(regs).regs[0])
+#define REG_IP(regs) ((u64)(regs).pc)
+#define REG_SYSCALL_NR(regs) ((u64)(regs).regs[8])
+
+// Copied from the Linux kernel arch/arm64/include/asm/memory.h
+// FIXME: what about a 32bit task?
+
+#define TASK_SIZE (1ULL << 39)
+
+#define AT_VECTOR_SIZE 40
+
+typedef UserAarch64RegsEntry UserRegsEntry;
+
+#define CORE_ENTRY__MARCH CORE_ENTRY__MARCH__AARCH64
+
+#define CORE_THREAD_ARCH_INFO(core) core->ti_aarch64
+
+#define TI_SP(core) ((core)->ti_aarch64->gpregs->sp)
+
+typedef uint64_t auxv_t;
+typedef uint64_t tls_t;
+
+static inline void *decode_pointer(uint64_t v) { return (void*)v; }
+static inline uint64_t encode_pointer(void *p) { return (uint64_t)p; }
+
+#endif /* __CR_ASM_TYPES_H__ */
diff --git a/arch/aarch64/include/asm/vdso.h b/arch/aarch64/include/asm/vdso.h
new file mode 100644
index 000000000..f8d1556de
--- /dev/null
+++ b/arch/aarch64/include/asm/vdso.h
@@ -0,0 +1,158 @@
+#ifndef __CR_ASM_VDSO_H__
+#define __CR_ASM_VDSO_H__
+
+#include
+
+#include "asm/int.h"
+#include "protobuf/vma.pb-c.h"
+
+struct parasite_ctl;
+struct vm_area_list;
+
+#define VDSO_PROT (PROT_READ | PROT_EXEC)
+#define VVAR_PROT (PROT_READ)
+
+#define VDSO_BAD_ADDR (-1ul)
+#define VVAR_BAD_ADDR VDSO_BAD_ADDR
+#define VDSO_BAD_PFN (-1ull)
+#define VVAR_BAD_PFN VDSO_BAD_PFN
+
+struct vdso_symbol {
+ char name[32];
+ unsigned long offset;
+};
+
+#define VDSO_SYMBOL_INIT { .offset = VDSO_BAD_ADDR, }
+
+/* Check if symbol present in symtable */
+static inline bool vdso_symbol_empty(struct vdso_symbol *s)
+{
+ return s->offset == VDSO_BAD_ADDR && s->name[0] == '\0';
+}
+
+/*
+ * This is a minimal amount of symbols
+ * we should support at the moment.
+ */
+enum {
+ VDSO_SYMBOL_CLOCK_GETRES,
+ VDSO_SYMBOL_CLOCK_GETTIME,
+ VDSO_SYMBOL_GETTIMEOFDAY,
+ VDSO_SYMBOL_RT_SIGRETURN,
+
+ VDSO_SYMBOL_MAX
+};
+
+struct vdso_symtable {
+ unsigned long vma_start;
+ unsigned long vma_end;
+ unsigned long vvar_start;
+ unsigned long vvar_end;
+ struct vdso_symbol symbols[VDSO_SYMBOL_MAX];
+};
+
+#define VDSO_SYMTABLE_INIT \
+ { \
+ .vma_start = VDSO_BAD_ADDR, \
+ .vma_end = VDSO_BAD_ADDR, \
+ .vvar_start = VVAR_BAD_ADDR, \
+ .vvar_end = VVAR_BAD_ADDR, \
+ .symbols = { \
+ [0 ... VDSO_SYMBOL_MAX - 1] = \
+ (struct vdso_symbol)VDSO_SYMBOL_INIT, \
+ }, \
+ }
+
+/* Size of VMA associated with vdso */
+static inline unsigned long vdso_vma_size(struct vdso_symtable *t)
+{
+ return t->vma_end - t->vma_start;
+}
+
+static inline unsigned long vvar_vma_size(struct vdso_symtable *t)
+{
+ return t->vvar_end - t->vvar_start;
+}
+/*
+ * Special mark which allows to identify runtime vdso where
+ * calls from proxy vdso are redirected. This mark usually
+ * placed at the start of vdso area where Elf header lives.
+ * Since such runtime vdso is solevey used by proxy and
+ * nobody else is supposed to access it, it's more-less
+ * safe to screw the Elf header with @signature and
+ * @proxy_addr.
+ *
+ * The @proxy_addr deserves a few comments. When we redirect
+ * the calls from proxy to runtime vdso, on next checkpoint
+ * it won't be possible to find which VMA is proxy, thus
+ * we save its address in the member.
+ */
+struct vdso_mark {
+ u64 signature;
+ unsigned long proxy_vdso_addr;
+
+ unsigned long version;
+
+ /*
+ * In case of new vDSO format the VVAR area address
+ * neeed for easier discovering where it lives without
+ * relying on procfs output.
+ */
+ unsigned long proxy_vvar_addr;
+};
+
+#define VDSO_MARK_SIGNATURE (0x6f73647675697263ULL) /* Magic number (criuvdso) */
+#define VDSO_MARK_SIGNATURE_V2 (0x4f53447675697263ULL) /* Magic number (criuvDSO) */
+#define VDSO_MARK_CUR_VERSION (2)
+
+static inline void vdso_put_mark(void *where, unsigned long proxy_vdso_addr, unsigned long proxy_vvar_addr)
+{
+ struct vdso_mark *m = where;
+
+ m->signature = VDSO_MARK_SIGNATURE_V2;
+ m->proxy_vdso_addr = proxy_vdso_addr;
+ m->version = VDSO_MARK_CUR_VERSION;
+ m->proxy_vvar_addr = proxy_vvar_addr;
+}
+
+static inline bool is_vdso_mark(void *addr)
+{
+ struct vdso_mark *m = addr;
+
+ if (m->signature == VDSO_MARK_SIGNATURE_V2) {
+ /*
+ * New format
+ */
+ return true;
+ } else if (m->signature == VDSO_MARK_SIGNATURE) {
+ /*
+ * Old format -- simply extend the mark up
+ * to the version we support.
+ */
+ vdso_put_mark(m, m->proxy_vdso_addr, VVAR_BAD_ADDR);
+ return true;
+ }
+ return false;
+}
+
+#define VDSO_SYMBOL_CLOCK_GETRES_NAME "__kernel_clock_getres"
+#define VDSO_SYMBOL_CLOCK_GETTIME_NAME "__kernel_clock_gettime"
+#define VDSO_SYMBOL_GETTIMEOFDAY_NAME "__kernel_gettimeofday"
+#define VDSO_SYMBOL_RT_SIGRETURN_NAME "__kernel_rt_sigreturn"
+
+extern struct vdso_symtable vdso_sym_rt;
+extern u64 vdso_pfn;
+
+extern int vdso_init(void);
+extern int vdso_do_park(struct vdso_symtable *sym_rt, unsigned long park_at, unsigned long park_size);
+extern int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t);
+extern int vdso_proxify(char *who, struct vdso_symtable *sym_rt,
+ unsigned long vdso_rt_parked_at, size_t index,
+ VmaEntry *vmas, size_t nr_vmas);
+
+extern int vdso_redirect_calls(void *base_to, void *base_from, struct vdso_symtable *to, struct vdso_symtable *from);
+extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
+ struct vm_area_list *vma_area_list);
+extern void write_intraprocedure_branch(void *to, void *from);
+
+#endif /* __CR_ASM_VDSO_H__ */
diff --git a/criu/arch/aarch64/intraprocedure.S b/arch/aarch64/intraprocedure.S
similarity index 100%
rename from criu/arch/aarch64/intraprocedure.S
rename to arch/aarch64/intraprocedure.S
diff --git a/arch/aarch64/parasite-head.S b/arch/aarch64/parasite-head.S
new file mode 100644
index 000000000..7a359061c
--- /dev/null
+++ b/arch/aarch64/parasite-head.S
@@ -0,0 +1,21 @@
+#include "asm/linkage.h"
+#include "parasite.h"
+
+ .section .head.text, "ax"
+ENTRY(__export_parasite_head_start)
+ adr x2, __export_parasite_head_start // get the address of this instruction
+
+ ldr x0, __export_parasite_cmd
+
+ ldr x1, parasite_args_ptr
+ add x1, x1, x2 // fixup __export_parasite_args
+
+ bl parasite_service
+ brk #0 // the instruction BRK #0 generates the signal SIGTRAP in Linux
+
+parasite_args_ptr:
+ .quad __export_parasite_args
+
+__export_parasite_cmd:
+ .quad 0
+END(__export_parasite_head_start)
diff --git a/arch/aarch64/restorer.c b/arch/aarch64/restorer.c
new file mode 100644
index 000000000..2c61e2d03
--- /dev/null
+++ b/arch/aarch64/restorer.c
@@ -0,0 +1,15 @@
+#include
+
+#include "restorer.h"
+#include "asm/restorer.h"
+#include "asm/string.h"
+
+#include "syscall.h"
+#include "log.h"
+#include "asm/fpu.h"
+#include "cpu.h"
+
+int restore_nonsigframe_gpregs(UserRegsEntry *r)
+{
+ return 0;
+}
diff --git a/arch/aarch64/syscall-common.S b/arch/aarch64/syscall-common.S
new file mode 100644
index 000000000..81ec20f55
--- /dev/null
+++ b/arch/aarch64/syscall-common.S
@@ -0,0 +1,19 @@
+#include "asm/linkage.h"
+
+syscall_common:
+ svc #0
+ ret
+
+
+.macro syscall name, nr
+ ENTRY(\name)
+ mov x8, \nr
+ b syscall_common
+ END(\name)
+.endm
+
+
+ENTRY(__cr_restore_rt)
+ mov x8, __NR_rt_sigreturn
+ svc #0
+END(__cr_restore_rt)
diff --git a/arch/aarch64/vdso-pie.c b/arch/aarch64/vdso-pie.c
new file mode 100644
index 000000000..c6558378d
--- /dev/null
+++ b/arch/aarch64/vdso-pie.c
@@ -0,0 +1,428 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "asm/string.h"
+#include "asm/types.h"
+
+#include "compiler.h"
+#include "syscall.h"
+#include "image.h"
+#include "vdso.h"
+#include "vma.h"
+#include "log.h"
+#include "bug.h"
+
+#ifdef LOG_PREFIX
+# undef LOG_PREFIX
+#endif
+#define LOG_PREFIX "vdso: "
+
+int vdso_redirect_calls(void *base_to, void *base_from,
+ struct vdso_symtable *to,
+ struct vdso_symtable *from)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(to->symbols); i++) {
+ if (vdso_symbol_empty(&from->symbols[i]))
+ continue;
+
+ pr_debug("br: %lx/%lx -> %lx/%lx (index %d)\n",
+ (unsigned long)base_from, from->symbols[i].offset,
+ (unsigned long)base_to, to->symbols[i].offset, i);
+
+ write_intraprocedure_branch(base_to + to->symbols[i].offset,
+ base_from + from->symbols[i].offset);
+ }
+
+ return 0;
+}
+
+
+/* Check if pointer is out-of-bound */
+static bool __ptr_oob(void *ptr, void *start, size_t size)
+{
+ void *end = (void *)((unsigned long)start + size);
+ return ptr > end || ptr < start;
+}
+
+/*
+ * Elf hash, see format specification.
+ */
+static unsigned long elf_hash(const unsigned char *name)
+{
+ unsigned long h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000ul;
+ if (g)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t)
+{
+ Elf64_Phdr *dynamic = NULL, *load = NULL;
+ Elf64_Ehdr *ehdr = (void *)mem;
+ Elf64_Dyn *dyn_strtab = NULL;
+ Elf64_Dyn *dyn_symtab = NULL;
+ Elf64_Dyn *dyn_strsz = NULL;
+ Elf64_Dyn *dyn_syment = NULL;
+ Elf64_Dyn *dyn_hash = NULL;
+ Elf64_Word *hash = NULL;
+ Elf64_Phdr *phdr;
+ Elf64_Dyn *d;
+
+ Elf64_Word *bucket, *chain;
+ Elf64_Word nbucket, nchain;
+
+ /*
+ * See Elf specification for this magic values.
+ */
+ const char elf_ident[] = {
+ 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ const char *vdso_symbols[VDSO_SYMBOL_MAX] = {
+ [VDSO_SYMBOL_CLOCK_GETRES] = VDSO_SYMBOL_CLOCK_GETRES_NAME,
+ [VDSO_SYMBOL_CLOCK_GETTIME] = VDSO_SYMBOL_CLOCK_GETTIME_NAME,
+ [VDSO_SYMBOL_GETTIMEOFDAY] = VDSO_SYMBOL_GETTIMEOFDAY_NAME,
+ [VDSO_SYMBOL_RT_SIGRETURN] = VDSO_SYMBOL_RT_SIGRETURN_NAME,
+ };
+
+ char *dynsymbol_names;
+ unsigned int i, j, k;
+
+ BUILD_BUG_ON(sizeof(elf_ident) != sizeof(ehdr->e_ident));
+
+ pr_debug("Parsing at %lx %lx\n", (long)mem, (long)mem + (long)size);
+
+ /*
+ * Make sure it's a file we support.
+ */
+ if (builtin_memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))) {
+ pr_err("Elf header magic mismatch\n");
+ return -EINVAL;
+ }
+
+ /*
+ * We need PT_LOAD and PT_DYNAMIC here. Each once.
+ */
+ phdr = (void *)&mem[ehdr->e_phoff];
+ for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+ if (__ptr_oob(phdr, mem, size))
+ goto err_oob;
+ switch (phdr->p_type) {
+ case PT_DYNAMIC:
+ if (dynamic) {
+ pr_err("Second PT_DYNAMIC header\n");
+ return -EINVAL;
+ }
+ dynamic = phdr;
+ break;
+ case PT_LOAD:
+ if (load) {
+ pr_err("Second PT_LOAD header\n");
+ return -EINVAL;
+ }
+ load = phdr;
+ break;
+ }
+ }
+
+ if (!load || !dynamic) {
+ pr_err("One of obligated program headers is missed\n");
+ return -EINVAL;
+ }
+
+ pr_debug("PT_LOAD p_vaddr: %lx\n", (unsigned long)load->p_vaddr);
+
+ /*
+ * Dynamic section tags should provide us the rest of information
+ * needed. Note that we're interested in a small set of tags.
+ */
+ d = (void *)&mem[dynamic->p_offset];
+ for (i = 0; i < dynamic->p_filesz / sizeof(*d); i++, d++) {
+ if (__ptr_oob(d, mem, size))
+ goto err_oob;
+
+ if (d->d_tag == DT_NULL) {
+ break;
+ } else if (d->d_tag == DT_STRTAB) {
+ dyn_strtab = d;
+ pr_debug("DT_STRTAB: %p\n", (void *)d->d_un.d_ptr);
+ } else if (d->d_tag == DT_SYMTAB) {
+ dyn_symtab = d;
+ pr_debug("DT_SYMTAB: %p\n", (void *)d->d_un.d_ptr);
+ } else if (d->d_tag == DT_STRSZ) {
+ dyn_strsz = d;
+ pr_debug("DT_STRSZ: %lu\n", (unsigned long)d->d_un.d_val);
+ } else if (d->d_tag == DT_SYMENT) {
+ dyn_syment = d;
+ pr_debug("DT_SYMENT: %lu\n", (unsigned long)d->d_un.d_val);
+ } else if (d->d_tag == DT_HASH) {
+ dyn_hash = d;
+ pr_debug("DT_HASH: %p\n", (void *)d->d_un.d_ptr);
+ }
+ }
+
+ if (!dyn_strtab || !dyn_symtab || !dyn_strsz || !dyn_syment || !dyn_hash) {
+ pr_err("Not all dynamic entries are present\n");
+ return -EINVAL;
+ }
+
+ dynsymbol_names = &mem[dyn_strtab->d_un.d_val - load->p_vaddr];
+ if (__ptr_oob(dynsymbol_names, mem, size))
+ goto err_oob;
+
+ hash = (void *)&mem[(unsigned long)dyn_hash->d_un.d_ptr - (unsigned long)load->p_vaddr];
+ if (__ptr_oob(hash, mem, size))
+ goto err_oob;
+
+ nbucket = hash[0];
+ nchain = hash[1];
+ bucket = &hash[2];
+ chain = &hash[nbucket + 2];
+
+ pr_debug("nbucket %lu nchain %lu bucket %p chain %p\n",
+ (long)nbucket, (long)nchain, bucket, chain);
+
+ for (i = 0; i < ARRAY_SIZE(vdso_symbols); i++) {
+ k = elf_hash((const unsigned char *)vdso_symbols[i]);
+
+ for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) {
+ Elf64_Sym *sym = (void *)&mem[dyn_symtab->d_un.d_ptr - load->p_vaddr];
+ char *name;
+
+ sym = &sym[j];
+ if (__ptr_oob(sym, mem, size))
+ continue;
+
+ if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC &&
+ ELF64_ST_BIND(sym->st_info) != STB_GLOBAL)
+ continue;
+
+ name = &dynsymbol_names[sym->st_name];
+ if (__ptr_oob(name, mem, size))
+ continue;
+
+ if (builtin_strcmp(name, vdso_symbols[i]))
+ continue;
+
+ builtin_memcpy(t->symbols[i].name, name, sizeof(t->symbols[i].name));
+ t->symbols[i].offset = (unsigned long)sym->st_value - load->p_vaddr;
+ break;
+ }
+ }
+
+ return 0;
+
+err_oob:
+ pr_err("Corrupted Elf data\n");
+ return -EFAULT;
+}
+
+static int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size)
+{
+ unsigned long addr;
+
+ pr_debug("Remap %s %lx -> %lx\n", who, from, to);
+
+ addr = sys_mremap(from, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, to);
+ if (addr != to) {
+ pr_err("Unable to remap %lx -> %lx %lx\n",
+ from, to, addr);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Park runtime vDSO in some safe place where it can be accessible from restorer */
+int vdso_do_park(struct vdso_symtable *sym_rt, unsigned long park_at, unsigned long park_size)
+{
+ int ret;
+
+ BUG_ON((vdso_vma_size(sym_rt) + vvar_vma_size(sym_rt)) < park_size);
+
+ if (sym_rt->vvar_start != VDSO_BAD_ADDR) {
+ if (sym_rt->vma_start < sym_rt->vvar_start) {
+ ret = vdso_remap("rt-vdso", sym_rt->vma_start,
+ park_at, vdso_vma_size(sym_rt));
+ park_at += vdso_vma_size(sym_rt);
+ ret |= vdso_remap("rt-vvar", sym_rt->vvar_start,
+ park_at, vvar_vma_size(sym_rt));
+ } else {
+ ret = vdso_remap("rt-vvar", sym_rt->vvar_start,
+ park_at, vvar_vma_size(sym_rt));
+ park_at += vvar_vma_size(sym_rt);
+ ret |= vdso_remap("rt-vdso", sym_rt->vma_start,
+ park_at, vdso_vma_size(sym_rt));
+ }
+ } else
+ ret = vdso_remap("rt-vdso", sym_rt->vma_start,
+ park_at, vdso_vma_size(sym_rt));
+ return ret;
+}
+
+int vdso_proxify(char *who, struct vdso_symtable *sym_rt,
+ unsigned long vdso_rt_parked_at, size_t index,
+ VmaEntry *vmas, size_t nr_vmas)
+{
+ VmaEntry *vma_vdso = NULL, *vma_vvar = NULL;
+ struct vdso_symtable s = VDSO_SYMTABLE_INIT;
+ bool remap_rt = false;
+
+ /*
+ * Figue out which kind of vdso tuple we get.
+ */
+ if (vma_entry_is(&vmas[index], VMA_AREA_VDSO))
+ vma_vdso = &vmas[index];
+ else if (vma_entry_is(&vmas[index], VMA_AREA_VVAR))
+ vma_vvar = &vmas[index];
+
+ if (index < (nr_vmas - 1)) {
+ if (vma_entry_is(&vmas[index + 1], VMA_AREA_VDSO))
+ vma_vdso = &vmas[index + 1];
+ else if (vma_entry_is(&vmas[index + 1], VMA_AREA_VVAR))
+ vma_vvar = &vmas[index + 1];
+ }
+
+ if (!vma_vdso) {
+ pr_err("Can't find vDSO area in image\n");
+ return -1;
+ }
+
+ /*
+ * vDSO mark overwrites Elf program header of proxy vDSO thus
+ * it must never ever be greater in size.
+ */
+ BUILD_BUG_ON(sizeof(struct vdso_mark) > sizeof(Elf64_Phdr));
+
+ /*
+ * Find symbols in vDSO zone read from image.
+ */
+ if (vdso_fill_symtable((void *)vma_vdso->start, vma_entry_len(vma_vdso), &s))
+ return -1;
+
+ /*
+ * Proxification strategy
+ *
+ * - There might be two vDSO zones: vdso code and optionally vvar data
+ * - To be able to use in-place remapping we need
+ *
+ * a) Size and order of vDSO zones are to match
+ * b) Symbols offsets must match
+ * c) Have same number of vDSO zones
+ */
+ if (vma_entry_len(vma_vdso) == vdso_vma_size(sym_rt)) {
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(s.symbols); i++) {
+ if (s.symbols[i].offset != sym_rt->symbols[i].offset)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(s.symbols)) {
+ if (vma_vvar && sym_rt->vvar_start != VVAR_BAD_ADDR) {
+ remap_rt = (vvar_vma_size(sym_rt) == vma_entry_len(vma_vvar));
+ if (remap_rt) {
+ long delta_rt = sym_rt->vvar_start - sym_rt->vma_start;
+ long delta_this = vma_vvar->start - vma_vdso->start;
+
+ remap_rt = (delta_rt ^ delta_this) < 0 ? false : true;
+ }
+ } else
+ remap_rt = true;
+ }
+ }
+
+ pr_debug("image [vdso] %lx-%lx [vvar] %lx-%lx\n",
+ vma_vdso->start, vma_vdso->end,
+ vma_vvar ? vma_vvar->start : VVAR_BAD_ADDR,
+ vma_vvar ? vma_vvar->end : VVAR_BAD_ADDR);
+
+ /*
+ * Easy case -- the vdso from image has same offsets, order and size
+ * as runtime, so we simply remap runtime vdso to dumpee position
+ * without generating any proxy.
+ *
+ * Note we may remap VVAR vdso as well which might not yet been mapped
+ * by a caller code. So drop VMA_AREA_REGULAR from it and caller would
+ * not touch it anymore.
+ */
+ if (remap_rt) {
+ int ret = 0;
+
+ pr_info("Runtime vdso/vvar matches dumpee, remap inplace\n");
+
+ if (sys_munmap((void *)vma_vdso->start, vma_entry_len(vma_vdso))) {
+ pr_err("Failed to unmap %s\n", who);
+ return -1;
+ }
+
+ if (vma_vvar) {
+ if (sys_munmap((void *)vma_vvar->start, vma_entry_len(vma_vvar))) {
+ pr_err("Failed to unmap %s\n", who);
+ return -1;
+ }
+
+ if (vma_vdso->start < vma_vvar->start) {
+ ret = vdso_remap(who, vdso_rt_parked_at, vma_vdso->start, vdso_vma_size(sym_rt));
+ vdso_rt_parked_at += vdso_vma_size(sym_rt);
+ ret |= vdso_remap(who, vdso_rt_parked_at, vma_vvar->start, vvar_vma_size(sym_rt));
+ } else {
+ ret = vdso_remap(who, vdso_rt_parked_at, vma_vvar->start, vvar_vma_size(sym_rt));
+ vdso_rt_parked_at += vvar_vma_size(sym_rt);
+ ret |= vdso_remap(who, vdso_rt_parked_at, vma_vdso->start, vdso_vma_size(sym_rt));
+ }
+ } else
+ ret = vdso_remap(who, vdso_rt_parked_at, vma_vdso->start, vdso_vma_size(sym_rt));
+
+ return ret;
+ }
+
+ /*
+ * Now complex case -- we need to proxify calls. We redirect
+ * calls from dumpee vdso to runtime vdso, making dumpee
+ * to operate as proxy vdso.
+ */
+ pr_info("Runtime vdso mismatches dumpee, generate proxy\n");
+
+ /*
+ * Don't forget to shift if vvar is before vdso.
+ */
+ if (sym_rt->vvar_start != VDSO_BAD_ADDR &&
+ sym_rt->vvar_start < sym_rt->vma_start)
+ vdso_rt_parked_at += vvar_vma_size(sym_rt);
+
+ if (vdso_redirect_calls((void *)vdso_rt_parked_at,
+ (void *)vma_vdso->start,
+ sym_rt, &s)) {
+ pr_err("Failed to proxify dumpee contents\n");
+ return -1;
+ }
+
+ /*
+ * Put a special mark into runtime vdso, thus at next checkpoint
+ * routine we could detect this vdso and do not dump it, since
+ * it's auto-generated every new session if proxy required.
+ */
+ sys_mprotect((void *)vdso_rt_parked_at, vdso_vma_size(sym_rt), PROT_WRITE);
+ vdso_put_mark((void *)vdso_rt_parked_at, vma_vdso->start, vma_vvar ? vma_vvar->start : VVAR_BAD_ADDR);
+ sys_mprotect((void *)vdso_rt_parked_at, vdso_vma_size(sym_rt), VDSO_PROT);
+ return 0;
+}
diff --git a/arch/aarch64/vdso.c b/arch/aarch64/vdso.c
new file mode 100644
index 000000000..43d9637f0
--- /dev/null
+++ b/arch/aarch64/vdso.c
@@ -0,0 +1,309 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "asm/types.h"
+#include "asm/parasite-syscall.h"
+
+#include "parasite-syscall.h"
+#include "parasite.h"
+#include "compiler.h"
+#include "kerndat.h"
+#include "vdso.h"
+#include "util.h"
+#include "log.h"
+#include "mem.h"
+#include "vma.h"
+
+#ifdef LOG_PREFIX
+# undef LOG_PREFIX
+#endif
+#define LOG_PREFIX "vdso: "
+
+struct vdso_symtable vdso_sym_rt = VDSO_SYMTABLE_INIT;
+u64 vdso_pfn = VDSO_BAD_PFN;
+/*
+ * The VMAs list might have proxy vdso/vvar areas left
+ * from previous dump/restore cycle so we need to detect
+ * them and eliminated from the VMAs list, they will be
+ * generated again on restore if needed.
+ */
+int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
+ struct vm_area_list *vma_area_list)
+{
+ unsigned long proxy_vdso_addr = VDSO_BAD_ADDR;
+ unsigned long proxy_vvar_addr = VVAR_BAD_ADDR;
+ struct vma_area *proxy_vdso_marked = NULL;
+ struct vma_area *proxy_vvar_marked = NULL;
+ struct parasite_vdso_vma_entry *args;
+ struct vma_area *vma;
+ int fd, ret = -1;
+ off_t off;
+ u64 pfn;
+
+ args = parasite_args(ctl, struct parasite_vdso_vma_entry);
+ fd = open_proc(pid, "pagemap");
+ if (fd < 0)
+ return -1;
+
+ list_for_each_entry(vma, &vma_area_list->h, list) {
+ if (!vma_area_is(vma, VMA_AREA_REGULAR))
+ continue;
+
+ if (vma_area_is(vma, VMA_FILE_SHARED) ||
+ vma_area_is(vma, VMA_FILE_PRIVATE))
+ continue;
+ /*
+ * It might be possible VVAR area from marked
+ * vDSO zone, we need to detect it earlier than
+ * VDSO_PROT test because VVAR_PROT is a subset
+ * of it but don't yield continue here,
+ * sigh... what a mess.
+ */
+ BUILD_BUG_ON(!(VDSO_PROT & VVAR_PROT));
+
+ if ((vma->e->prot & VVAR_PROT) == VVAR_PROT) {
+ if (proxy_vvar_addr != VVAR_BAD_ADDR &&
+ proxy_vvar_addr == vma->e->start) {
+ BUG_ON(proxy_vvar_marked);
+ proxy_vvar_marked = vma;
+ continue;
+ }
+ }
+
+ if ((vma->e->prot & VDSO_PROT) != VDSO_PROT)
+ continue;
+
+ if (vma->e->prot != VDSO_PROT) {
+ pr_debug("Dropping %lx using extra protection test\n",
+ vma->e->start);
+ continue;
+ }
+
+ if (vma->e->start > TASK_SIZE)
+ continue;
+
+ if (vma->e->flags & MAP_GROWSDOWN)
+ continue;
+
+ /*
+ * I need to poke every potentially marked vma,
+ * otherwise if task never called for vdso functions
+ * page frame number won't be reported.
+ */
+ args->start = vma->e->start;
+ args->len = vma_area_len(vma);
+
+ if (parasite_execute_daemon(PARASITE_CMD_CHECK_VDSO_MARK, ctl)) {
+ pr_err("vdso: Parasite failed to poke for mark\n");
+ ret = -1;
+ goto err;
+ }
+
+ /*
+ * Defer handling marked vdso until we walked over
+ * all vmas and restore potentially remapped vDSO
+ * area status.
+ */
+ if (unlikely(args->is_marked)) {
+ if (proxy_vdso_marked) {
+ pr_err("Ow! Second vdso mark detected!\n");
+ ret = -1;
+ goto err;
+ }
+ proxy_vdso_marked = vma;
+ proxy_vdso_addr = args->proxy_vdso_addr;
+ proxy_vvar_addr = args->proxy_vvar_addr;
+ continue;
+ }
+
+ off = (vma->e->start / PAGE_SIZE) * sizeof(u64);
+ ret = pread(fd, &pfn, sizeof(pfn), off);
+ if (ret < 0 || ret != sizeof(pfn)) {
+ pr_perror("Can't read pme for pid %d", pid);
+ ret = -1;
+ goto err;
+ }
+
+ pfn = PME_PFRAME(pfn);
+ if (!pfn) {
+ pr_err("Unexpected page fram number 0 for pid %d\n", pid);
+ ret = -1;
+ goto err;
+ }
+
+ /*
+ * Setup proper VMA status. Note starting with 3.16
+ * the [vdso]/[vvar] marks are reported correctly
+ * even when they are remapped into a new place,
+ * but only since that particular version of the
+ * kernel!
+ */
+ if (pfn == vdso_pfn) {
+ if (!vma_area_is(vma, VMA_AREA_VDSO)) {
+ pr_debug("vdso: Restore vDSO status by pfn at %lx\n",
+ (long)vma->e->start);
+ vma->e->status |= VMA_AREA_VDSO;
+ }
+ } else {
+ if (unlikely(vma_area_is(vma, VMA_AREA_VDSO))) {
+ pr_debug("vdso: Drop mishinted vDSO status at %lx\n",
+ (long)vma->e->start);
+ vma->e->status &= ~VMA_AREA_VDSO;
+ }
+ }
+ }
+
+ /*
+ * There is marked vdso, it means such vdso is autogenerated
+ * and must be dropped from vma list.
+ */
+ if (proxy_vdso_marked) {
+ pr_debug("vdso: Found marked at %lx (proxy vDSO at %lx VVAR at %lx)\n",
+ (long)proxy_vdso_marked->e->start,
+ (long)proxy_vdso_addr, (long)proxy_vvar_addr);
+
+ /*
+ * Don't forget to restore the proxy vdso/vvar status, since
+ * it's unknown to the kernel.
+ */
+ list_for_each_entry(vma, &vma_area_list->h, list) {
+ if (vma->e->start == proxy_vdso_addr) {
+ vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VDSO;
+ pr_debug("vdso: Restore proxy vDSO status at %lx\n",
+ (long)vma->e->start);
+ } else if (vma->e->start == proxy_vvar_addr) {
+ vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VVAR;
+ pr_debug("vdso: Restore proxy VVAR status at %lx\n",
+ (long)vma->e->start);
+ }
+ }
+
+ pr_debug("vdso: Droppping marked vdso at %lx\n",
+ (long)proxy_vdso_marked->e->start);
+ list_del(&proxy_vdso_marked->list);
+ xfree(proxy_vdso_marked);
+ vma_area_list->nr--;
+
+ if (proxy_vvar_marked) {
+ pr_debug("vdso: Droppping marked vvar at %lx\n",
+ (long)proxy_vvar_marked->e->start);
+ list_del(&proxy_vvar_marked->list);
+ xfree(proxy_vvar_marked);
+ vma_area_list->nr--;
+ }
+ }
+ ret = 0;
+err:
+ close(fd);
+ return ret;
+}
+
+static int vdso_fill_self_symtable(struct vdso_symtable *s)
+{
+ char buf[512];
+ int ret = -1;
+ FILE *maps;
+
+ *s = (struct vdso_symtable)VDSO_SYMTABLE_INIT;
+
+ maps = fopen_proc(PROC_SELF, "maps");
+ if (!maps) {
+ pr_perror("Can't open self-vma");
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), maps)) {
+ unsigned long start, end;
+ char *has_vdso, *has_vvar;
+
+ has_vdso = strstr(buf, "[vdso]");
+ if (!has_vdso)
+ has_vvar = strstr(buf, "[vvar]");
+ else
+ has_vvar = NULL;
+
+ if (!has_vdso && !has_vvar)
+ continue;
+
+ ret = sscanf(buf, "%lx-%lx", &start, &end);
+ if (ret != 2) {
+ ret = -1;
+ pr_err("Can't find vDSO/VVAR bounds\n");
+ goto err;
+ }
+
+ if (has_vdso) {
+ if (s->vma_start != VDSO_BAD_ADDR) {
+ pr_err("Got second vDSO entry\n");
+ ret = -1;
+ goto err;
+ }
+ s->vma_start = start;
+ s->vma_end = end;
+
+ ret = vdso_fill_symtable((void *)start, end - start, s);
+ if (ret)
+ goto err;
+ } else {
+ if (s->vvar_start != VVAR_BAD_ADDR) {
+ pr_err("Got second VVAR entry\n");
+ ret = -1;
+ goto err;
+ }
+ s->vvar_start = start;
+ s->vvar_end = end;
+ }
+ }
+
+ /*
+ * Validate its structure -- for new vDSO format the
+ * structure must be like
+ *
+ * 7fff1f5fd000-7fff1f5fe000 r-xp 00000000 00:00 0 [vdso]
+ * 7fff1f5fe000-7fff1f600000 r--p 00000000 00:00 0 [vvar]
+ *
+ * The areas may be in reverse order.
+ *
+ * 7fffc3502000-7fffc3504000 r--p 00000000 00:00 0 [vvar]
+ * 7fffc3504000-7fffc3506000 r-xp 00000000 00:00 0 [vdso]
+ *
+ */
+ ret = 0;
+ if (s->vma_start != VDSO_BAD_ADDR) {
+ if (s->vvar_start != VVAR_BAD_ADDR) {
+ if (s->vma_end != s->vvar_start &&
+ s->vvar_end != s->vma_start) {
+ ret = -1;
+ pr_err("Unexpected rt vDSO area bounds\n");
+ goto err;
+ }
+ }
+ } else {
+ ret = -1;
+ pr_err("Can't find rt vDSO\n");
+ goto err;
+ }
+
+ pr_debug("rt [vdso] %lx-%lx [vvar] %lx-%lx\n",
+ s->vma_start, s->vma_end,
+ s->vvar_start, s->vvar_end);
+
+err:
+ fclose(maps);
+ return ret;
+}
+
+int vdso_init(void)
+{
+ if (vdso_fill_self_symtable(&vdso_sym_rt))
+ return -1;
+ return vaddr_to_pfn(vdso_sym_rt.vma_start, &vdso_pfn);
+}
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
new file mode 100644
index 000000000..2359a2c0e
--- /dev/null
+++ b/arch/arm/Makefile
@@ -0,0 +1,59 @@
+targets += syscalls
+targets += crtools
+
+SYS-ASM := syscalls.S
+
+syscalls-asm-y += $(SYS-ASM:.S=).o
+crtools-obj-y += crtools.o
+crtools-obj-y += cpu.o
+
+SYS-DEF := syscall.def
+SYS-ASM-COMMON := syscall-common.S
+SYS-TYPES := include/syscall-types.h
+
+SYS-CODES := include/syscall-codes.h
+SYS-PROTO := include/syscall.h
+
+SYS-GEN := ../scripts/arm/gen-syscalls.pl
+SYS-GEN-TBL := ../scripts/arm/gen-sys-exec-tbl.pl
+
+SYS-EXEC-TBL := sys-exec-tbl.c
+
+syscalls-asm-y-asmflags += -fpie -Wstrict-prototypes -Wa,--noexecstack
+syscalls-asm-y-asmflags += -nostdlib -fomit-frame-pointer -I$(obj)
+ASMFLAGS += -D__ASSEMBLY__
+
+ARCH_BITS := 32
+
+$(obj)/$(SYS-ASM): $(obj)/$(SYS-GEN) $(obj)/$(SYS-DEF) $(obj)/$(SYS-ASM-COMMON) $(SYS-TYPES)
+ $(E) " GEN " $@
+ $(Q) perl \
+ $(obj)/$(SYS-GEN) \
+ $(obj)/$(SYS-DEF) \
+ $(SYS-CODES) \
+ $(SYS-PROTO) \
+ $(obj)/$(SYS-ASM) \
+ $(SYS-ASM-COMMON) \
+ $(SYS-TYPES) \
+ $(ARCH_BITS)
+
+$(obj)/syscalls.o: $(obj)/$(SYS-ASM)
+
+$(obj)/$(SYS-EXEC-TBL): $(obj)/$(SYS-GEN-TBL) $(obj)/$(SYS-DEF)
+ $(E) " GEN " $@
+ $(Q) perl \
+ $(obj)/$(SYS-GEN-TBL) \
+ $(obj)/$(SYS-DEF) \
+ $(obj)/$(SYS-EXEC-TBL) \
+ $(ARCH_BITS)
+
+_all += $(obj)/$(SYS-EXEC-TBL)
+
+cleanup-y += $(obj)/$(SYS-EXEC-TBL) $(obj)/$(SYS-ASM)
+cleanup-y += $(SYS-CODES)
+cleanup-y += $(SYS-PROTO)
+
+ifneq ($(MAKECMDGOALS),clean)
+deps-after := $(obj)/$(SYS-ASM)
+incdeps := y
+endif
diff --git a/arch/arm/cpu.c b/arch/arm/cpu.c
new file mode 100644
index 000000000..040fe14fc
--- /dev/null
+++ b/arch/arm/cpu.c
@@ -0,0 +1,45 @@
+#undef LOG_PREFIX
+#define LOG_PREFIX "cpu: "
+
+#include
+#include "cpu.h"
+
+bool cpu_has_feature(unsigned int feature)
+{
+ return false;
+}
+
+int cpu_init(void)
+{
+ return 0;
+}
+
+int cpu_dump_cpuinfo(void)
+{
+ return 0;
+}
+
+int cpu_validate_cpuinfo(void)
+{
+ return 0;
+}
+
+int cpu_dump_cpuinfo_single(void)
+{
+ return -ENOTSUP;
+}
+
+int cpu_validate_image_cpuinfo_single(void)
+{
+ return -ENOTSUP;
+}
+
+int cpuinfo_dump(void)
+{
+ return -ENOTSUP;
+}
+
+int cpuinfo_check(void)
+{
+ return -ENOTSUP;
+}
diff --git a/arch/arm/crtools.c b/arch/arm/crtools.c
new file mode 100644
index 000000000..a5420b197
--- /dev/null
+++ b/arch/arm/crtools.c
@@ -0,0 +1,249 @@
+#include
+#include
+
+#include "asm/types.h"
+#include "asm/restorer.h"
+#include "compiler.h"
+#include "ptrace.h"
+#include "asm/processor-flags.h"
+#include "protobuf.h"
+#include "protobuf/core.pb-c.h"
+#include "protobuf/creds.pb-c.h"
+#include "parasite-syscall.h"
+#include "syscall.h"
+#include "log.h"
+#include "util.h"
+#include "cpu.h"
+#include "elf.h"
+#include "parasite-syscall.h"
+#include "restorer.h"
+#include "errno.h"
+
+
+/*
+ * Injected syscall instruction
+ */
+const char code_syscall[] = {
+ 0x00, 0x00, 0x00, 0xef, /* SVC #0 */
+ 0xf0, 0x01, 0xf0, 0xe7 /* UDF #32 */
+};
+
+const int code_syscall_size = round_up(sizeof(code_syscall), sizeof(long));
+
+static inline void __check_code_syscall(void)
+{
+ BUILD_BUG_ON(sizeof(code_syscall) != BUILTIN_SYSCALL_SIZE);
+ BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
+}
+
+
+void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *regs)
+{
+ regs->ARM_pc = new_ip;
+ if (stack)
+ regs->ARM_sp = (unsigned long)stack;
+
+ /* Make sure flags are in known state */
+ regs->ARM_cpsr &= PSR_f | PSR_s | PSR_x | MODE32_BIT;
+}
+
+bool arch_can_dump_task(pid_t pid)
+{
+ /*
+ * TODO: Add proper check here
+ */
+ return true;
+}
+
+int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
+ unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5,
+ unsigned long arg6)
+{
+ user_regs_struct_t regs = ctl->orig.regs;
+ int err;
+
+ regs.ARM_r7 = (unsigned long)nr;
+ regs.ARM_r0 = arg1;
+ regs.ARM_r1 = arg2;
+ regs.ARM_r2 = arg3;
+ regs.ARM_r3 = arg4;
+ regs.ARM_r4 = arg5;
+ regs.ARM_r5 = arg6;
+
+ err = __parasite_execute_syscall(ctl, ®s);
+
+ *ret = regs.ARM_r0;
+ return err;
+}
+
+#define assign_reg(dst, src, e) dst->e = (__typeof__(dst->e))src.ARM_##e
+
+#define PTRACE_GETVFPREGS 27
+int get_task_regs(pid_t pid, user_regs_struct_t regs, CoreEntry *core)
+{
+ struct user_vfp vfp;
+ int ret = -1;
+
+ pr_info("Dumping GP/FPU registers for %d\n", pid);
+
+ if (ptrace(PTRACE_GETVFPREGS, pid, NULL, &vfp)) {
+ pr_perror("Can't obtain FPU registers for %d", pid);
+ goto err;
+ }
+
+ /* Did we come from a system call? */
+ if ((int)regs.ARM_ORIG_r0 >= 0) {
+ /* Restart the system call */
+ switch ((long)(int)regs.ARM_r0) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ regs.ARM_r0 = regs.ARM_ORIG_r0;
+ regs.ARM_pc -= 4;
+ break;
+ case -ERESTART_RESTARTBLOCK:
+ regs.ARM_r0 = __NR_restart_syscall;
+ regs.ARM_pc -= 4;
+ break;
+ }
+ }
+
+
+ // Save the ARM CPU state
+
+ assign_reg(core->ti_arm->gpregs, regs, r0);
+ assign_reg(core->ti_arm->gpregs, regs, r1);
+ assign_reg(core->ti_arm->gpregs, regs, r2);
+ assign_reg(core->ti_arm->gpregs, regs, r3);
+ assign_reg(core->ti_arm->gpregs, regs, r4);
+ assign_reg(core->ti_arm->gpregs, regs, r5);
+ assign_reg(core->ti_arm->gpregs, regs, r6);
+ assign_reg(core->ti_arm->gpregs, regs, r7);
+ assign_reg(core->ti_arm->gpregs, regs, r8);
+ assign_reg(core->ti_arm->gpregs, regs, r9);
+ assign_reg(core->ti_arm->gpregs, regs, r10);
+ assign_reg(core->ti_arm->gpregs, regs, fp);
+ assign_reg(core->ti_arm->gpregs, regs, ip);
+ assign_reg(core->ti_arm->gpregs, regs, sp);
+ assign_reg(core->ti_arm->gpregs, regs, lr);
+ assign_reg(core->ti_arm->gpregs, regs, pc);
+ assign_reg(core->ti_arm->gpregs, regs, cpsr);
+ core->ti_arm->gpregs->orig_r0 = regs.ARM_ORIG_r0;
+
+
+ // Save the VFP state
+
+ memcpy(CORE_THREAD_ARCH_INFO(core)->fpstate->vfp_regs, &vfp.fpregs, sizeof(vfp.fpregs));
+ CORE_THREAD_ARCH_INFO(core)->fpstate->fpscr = vfp.fpscr;
+
+ ret = 0;
+
+err:
+ return ret;
+}
+
+int arch_alloc_thread_info(CoreEntry *core)
+{
+ ThreadInfoArm *ti_arm;
+ UserArmRegsEntry *gpregs;
+ UserArmVfpstateEntry *fpstate;
+
+ ti_arm = xmalloc(sizeof(*ti_arm));
+ if (!ti_arm)
+ goto err;
+ thread_info_arm__init(ti_arm);
+ core->ti_arm = ti_arm;
+
+ gpregs = xmalloc(sizeof(*gpregs));
+ user_arm_regs_entry__init(gpregs);
+ ti_arm->gpregs = gpregs;
+
+ fpstate = xmalloc(sizeof(*fpstate));
+ if (!fpstate)
+ goto err;
+ user_arm_vfpstate_entry__init(fpstate);
+ ti_arm->fpstate = fpstate;
+ fpstate->vfp_regs = xmalloc(32*sizeof(unsigned long long));
+ fpstate->n_vfp_regs = 32;
+ if (!fpstate->vfp_regs)
+ goto err;
+
+ return 0;
+err:
+ return -1;
+}
+
+void arch_free_thread_info(CoreEntry *core)
+{
+ if (CORE_THREAD_ARCH_INFO(core)) {
+ if (CORE_THREAD_ARCH_INFO(core)->fpstate) {
+ xfree(CORE_THREAD_ARCH_INFO(core)->fpstate->vfp_regs);
+ xfree(CORE_THREAD_ARCH_INFO(core)->fpstate);
+ }
+ xfree(CORE_THREAD_ARCH_INFO(core)->gpregs);
+ xfree(CORE_THREAD_ARCH_INFO(core));
+ CORE_THREAD_ARCH_INFO(core) = NULL;
+ }
+}
+
+int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
+{
+ struct aux_sigframe *aux = (struct aux_sigframe *)&sigframe->sig.uc.uc_regspace;
+
+ memcpy(&aux->vfp.ufp.fpregs, CORE_THREAD_ARCH_INFO(core)->fpstate->vfp_regs, sizeof(aux->vfp.ufp.fpregs));
+ aux->vfp.ufp.fpscr = CORE_THREAD_ARCH_INFO(core)->fpstate->fpscr;
+ aux->vfp.magic = VFP_MAGIC;
+ aux->vfp.size = VFP_STORAGE_SIZE;
+ return 0;
+}
+
+void *mmap_seized(struct parasite_ctl *ctl,
+ void *addr, size_t length, int prot,
+ int flags, int fd, off_t offset)
+{
+ unsigned long map;
+ int err;
+
+ if (offset & ~PAGE_MASK)
+ return 0;
+
+ err = syscall_seized(ctl, __NR_mmap2, &map,
+ (unsigned long)addr, length, prot, flags, fd, offset >> 12);
+ if (err < 0 || map > TASK_SIZE)
+ map = 0;
+
+ return (void *)map;
+}
+
+int restore_gpregs(struct rt_sigframe *f, UserArmRegsEntry *r)
+{
+#define CPREG1(d) f->sig.uc.uc_mcontext.arm_##d = r->d
+#define CPREG2(d, s) f->sig.uc.uc_mcontext.arm_##d = r->s
+
+ CPREG1(r0);
+ CPREG1(r1);
+ CPREG1(r2);
+ CPREG1(r3);
+ CPREG1(r4);
+ CPREG1(r5);
+ CPREG1(r6);
+ CPREG1(r7);
+ CPREG1(r8);
+ CPREG1(r9);
+ CPREG1(r10);
+ CPREG1(fp);
+ CPREG1(ip);
+ CPREG1(sp);
+ CPREG1(lr);
+ CPREG1(pc);
+ CPREG1(cpsr);
+
+#undef CPREG1
+#undef CPREG2
+
+ return 0;
+}
diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h
new file mode 100644
index 000000000..cd0df3772
--- /dev/null
+++ b/arch/arm/include/asm/atomic.h
@@ -0,0 +1,131 @@
+#ifndef __CR_ATOMIC_H__
+#define __CR_ATOMIC_H__
+
+#include "asm/processor.h"
+
+typedef struct {
+ int counter;
+} atomic_t;
+
+
+/* Copied from the Linux kernel header arch/arm/include/asm/atomic.h */
+
+#if defined(CONFIG_ARMV7)
+
+#define smp_mb() __asm__ __volatile__ ("dmb" : : : "memory")
+
+static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
+{
+ int oldval;
+ unsigned long res;
+
+ smp_mb();
+ prefetchw(&ptr->counter);
+
+ do {
+ __asm__ __volatile__("@ atomic_cmpxchg\n"
+ "ldrex %1, [%3]\n"
+ "mov %0, #0\n"
+ "teq %1, %4\n"
+ "strexeq %0, %5, [%3]\n"
+ : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter)
+ : "r" (&ptr->counter), "Ir" (old), "r" (new)
+ : "cc");
+ } while (res);
+
+ smp_mb();
+
+ return oldval;
+}
+
+#elif defined(CONFIG_ARMV6)
+
+/* SMP isn't supported for ARMv6 */
+
+#define smp_mb() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" : : "r" (0) : "memory")
+
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+ int ret;
+
+ ret = v->counter;
+ if (ret == old)
+ v->counter = new;
+
+ return ret;
+}
+
+#else
+
+#error ARM architecture version (CONFIG_ARMV*) not set or unsupported.
+
+#endif
+
+static inline int atomic_read(const atomic_t *v)
+{
+ return (*(volatile int *)&(v)->counter);
+}
+
+static inline void atomic_set(atomic_t *v, int i)
+{
+ v->counter = i;
+}
+
+#define atomic_get atomic_read
+
+static inline int atomic_add_return(int i, atomic_t *v)
+{
+ unsigned long tmp;
+ int result;
+
+ smp_mb();
+
+ __asm__ __volatile__("@ atomic_add_return\n"
+"1: ldrex %0, [%3]\n"
+" add %0, %0, %4\n"
+" strex %1, %0, [%3]\n"
+" teq %1, #0\n"
+" bne 1b\n"
+ : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+
+ smp_mb();
+
+ return result;
+}
+
+static inline int atomic_sub_return(int i, atomic_t *v)
+{
+ unsigned long tmp;
+ int result;
+
+ smp_mb();
+
+ __asm__ __volatile__("@ atomic_sub_return\n"
+"1: ldrex %0, [%3]\n"
+" sub %0, %0, %4\n"
+" strex %1, %0, [%3]\n"
+" teq %1, #0\n"
+" bne 1b\n"
+ : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+
+ smp_mb();
+
+ return result;
+}
+
+static inline int atomic_inc(atomic_t *v) { return atomic_add_return(1, v) - 1; }
+
+static inline int atomic_add(int val, atomic_t *v) { return atomic_add_return(val, v) - val; }
+
+static inline int atomic_dec(atomic_t *v) { return atomic_sub_return(1, v) + 1; }
+
+/* true if the result is 0, or false for all other cases. */
+#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
+
+#define atomic_inc_return(v) (atomic_add_return(1, v))
+
+#endif /* __CR_ATOMIC_H__ */
diff --git a/arch/arm/include/asm/bitops.h b/arch/arm/include/asm/bitops.h
new file mode 100644
index 000000000..5a750447f
--- /dev/null
+++ b/arch/arm/include/asm/bitops.h
@@ -0,0 +1,7 @@
+#ifndef __CR_ASM_BITOPS_H__
+#define __CR_ASM_BITOPS_H__
+
+#include "compiler.h"
+#include "asm-generic/bitops.h"
+
+#endif /* __CR_ASM_BITOPS_H__ */
diff --git a/include/common/arch/arm/asm/bitsperlong.h b/arch/arm/include/asm/bitsperlong.h
similarity index 100%
rename from include/common/arch/arm/asm/bitsperlong.h
rename to arch/arm/include/asm/bitsperlong.h
diff --git a/arch/arm/include/asm/cpu.h b/arch/arm/include/asm/cpu.h
new file mode 100644
index 000000000..59118c211
--- /dev/null
+++ b/arch/arm/include/asm/cpu.h
@@ -0,0 +1 @@
+#include
diff --git a/arch/arm/include/asm/dump.h b/arch/arm/include/asm/dump.h
new file mode 100644
index 000000000..ae1588da8
--- /dev/null
+++ b/arch/arm/include/asm/dump.h
@@ -0,0 +1,14 @@
+#ifndef __CR_ASM_DUMP_H__
+#define __CR_ASM_DUMP_H__
+
+extern int get_task_regs(pid_t pid, user_regs_struct_t regs, CoreEntry *core);
+extern int arch_alloc_thread_info(CoreEntry *core);
+extern void arch_free_thread_info(CoreEntry *core);
+
+
+static inline void core_put_tls(CoreEntry *core, tls_t tls)
+{
+ core->ti_arm->tls = tls;
+}
+
+#endif
diff --git a/compel/arch/arm/src/lib/include/uapi/asm/fpu.h b/arch/arm/include/asm/fpu.h
similarity index 100%
rename from compel/arch/arm/src/lib/include/uapi/asm/fpu.h
rename to arch/arm/include/asm/fpu.h
diff --git a/criu/arch/arm/include/asm/int.h b/arch/arm/include/asm/int.h
similarity index 100%
rename from criu/arch/arm/include/asm/int.h
rename to arch/arm/include/asm/int.h
diff --git a/arch/arm/include/asm/linkage.h b/arch/arm/include/asm/linkage.h
new file mode 100644
index 000000000..738064233
--- /dev/null
+++ b/arch/arm/include/asm/linkage.h
@@ -0,0 +1,24 @@
+#ifndef __CR_LINKAGE_H__
+#define __CR_LINKAGE_H__
+
+#ifdef __ASSEMBLY__
+
+#define __ALIGN .align 4, 0x00
+#define __ALIGN_STR ".align 4, 0x00"
+
+#define GLOBAL(name) \
+ .globl name; \
+ name:
+
+#define ENTRY(name) \
+ .globl name; \
+ .type name, #function; \
+ __ALIGN; \
+ name:
+
+#define END(sym) \
+ .size sym, . - sym
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __CR_LINKAGE_H__ */
diff --git a/arch/arm/include/asm/parasite-syscall.h b/arch/arm/include/asm/parasite-syscall.h
new file mode 100644
index 000000000..0c66bf992
--- /dev/null
+++ b/arch/arm/include/asm/parasite-syscall.h
@@ -0,0 +1,18 @@
+#ifndef __CR_ASM_PARASITE_SYSCALL_H__
+#define __CR_ASM_PARASITE_SYSCALL_H__
+
+
+#define ARCH_SI_TRAP TRAP_BRKPT
+
+
+extern const char code_syscall[];
+extern const int code_syscall_size;
+
+
+void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *regs);
+
+void *mmap_seized(struct parasite_ctl *ctl,
+ void *addr, size_t length, int prot,
+ int flags, int fd, off_t offset);
+
+#endif
diff --git a/arch/arm/include/asm/parasite.h b/arch/arm/include/asm/parasite.h
new file mode 100644
index 000000000..7f62bb9d2
--- /dev/null
+++ b/arch/arm/include/asm/parasite.h
@@ -0,0 +1,9 @@
+#ifndef __ASM_PARASITE_H__
+#define __ASM_PARASITE_H__
+
+static inline void arch_get_tls(tls_t *ptls)
+{
+ *ptls = ((tls_t (*)())0xffff0fe0)();
+}
+
+#endif
diff --git a/arch/arm/include/asm/processor-flags.h b/arch/arm/include/asm/processor-flags.h
new file mode 100644
index 000000000..fc00a9e64
--- /dev/null
+++ b/arch/arm/include/asm/processor-flags.h
@@ -0,0 +1,42 @@
+#ifndef __CR_PROCESSOR_FLAGS_H__
+#define __CR_PROCESSOR_FLAGS_H__
+
+/* Copied from the Linux kernel header arch/arm/include/uapi/asm/ptrace.h */
+
+/*
+ * PSR bits
+ */
+#define USR26_MODE 0x00000000
+#define FIQ26_MODE 0x00000001
+#define IRQ26_MODE 0x00000002
+#define SVC26_MODE 0x00000003
+#define USR_MODE 0x00000010
+#define FIQ_MODE 0x00000011
+#define IRQ_MODE 0x00000012
+#define SVC_MODE 0x00000013
+#define ABT_MODE 0x00000017
+#define UND_MODE 0x0000001b
+#define SYSTEM_MODE 0x0000001f
+#define MODE32_BIT 0x00000010
+#define MODE_MASK 0x0000001f
+#define PSR_T_BIT 0x00000020
+#define PSR_F_BIT 0x00000040
+#define PSR_I_BIT 0x00000080
+#define PSR_A_BIT 0x00000100
+#define PSR_E_BIT 0x00000200
+#define PSR_J_BIT 0x01000000
+#define PSR_Q_BIT 0x08000000
+#define PSR_V_BIT 0x10000000
+#define PSR_C_BIT 0x20000000
+#define PSR_Z_BIT 0x40000000
+#define PSR_N_BIT 0x80000000
+
+/*
+ * Groups of PSR bits
+ */
+#define PSR_f 0xff000000 /* Flags */
+#define PSR_s 0x00ff0000 /* Status */
+#define PSR_x 0x0000ff00 /* Extension */
+#define PSR_c 0x000000ff /* Control */
+
+#endif
diff --git a/arch/arm/include/asm/processor.h b/arch/arm/include/asm/processor.h
new file mode 100644
index 000000000..a390cfd32
--- /dev/null
+++ b/arch/arm/include/asm/processor.h
@@ -0,0 +1,28 @@
+#ifndef __CR_PROCESSOR_H__
+#define __CR_PROCESSOR_H__
+
+/* Copied from linux kernel arch/arm/include/asm/unified.h */
+
+#define WASM(instr) #instr
+
+/* Copied from linux kernel arch/arm/include/asm/processor.h */
+
+#define __ALT_SMP_ASM(smp, up) \
+ "9998: " smp "\n" \
+ " .pushsection \".alt.smp.init\", \"a\"\n" \
+ " .long 9998b\n" \
+ " " up "\n" \
+ " .popsection\n"
+
+static inline void prefetchw(const void *ptr)
+{
+ __asm__ __volatile__(
+ ".arch_extension mp\n"
+ __ALT_SMP_ASM(
+ WASM(pldw) "\t%a0",
+ WASM(pld) "\t%a0"
+ )
+ :: "p" (ptr));
+}
+
+#endif /* __CR_PROCESSOR_H__ */
diff --git a/arch/arm/include/asm/restore.h b/arch/arm/include/asm/restore.h
new file mode 100644
index 000000000..a1e66a5d5
--- /dev/null
+++ b/arch/arm/include/asm/restore.h
@@ -0,0 +1,29 @@
+#ifndef __CR_ASM_RESTORE_H__
+#define __CR_ASM_RESTORE_H__
+
+#include "asm/restorer.h"
+
+#include "protobuf/core.pb-c.h"
+
+#define JUMP_TO_RESTORER_BLOB(new_sp, restore_task_exec_start, \
+ task_args) \
+ asm volatile( \
+ "mov %%sp, %%%0 \n" \
+ "mov %%r1, %%%1 \n" \
+ "mov %%r0, %%%2 \n" \
+ "bx %%r1 \n" \
+ : \
+ : "r"(new_sp), \
+ "r"(restore_task_exec_start), \
+ "r"(task_args) \
+ : "sp", "r0", "r1", "memory")
+
+static inline void core_get_tls(CoreEntry *pcore, tls_t *ptls)
+{
+ *ptls = pcore->ti_arm->tls;
+}
+
+
+int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core);
+
+#endif
diff --git a/arch/arm/include/asm/restorer.h b/arch/arm/include/asm/restorer.h
new file mode 100644
index 000000000..8acb2d3e7
--- /dev/null
+++ b/arch/arm/include/asm/restorer.h
@@ -0,0 +1,163 @@
+#ifndef __CR_ASM_RESTORER_H__
+#define __CR_ASM_RESTORER_H__
+
+#include "asm/types.h"
+#include "protobuf/core.pb-c.h"
+
+/* Copied from the Linux kernel header arch/arm/include/asm/sigcontext.h */
+
+struct rt_sigcontext {
+ unsigned long trap_no;
+ unsigned long error_code;
+ unsigned long oldmask;
+ unsigned long arm_r0;
+ unsigned long arm_r1;
+ unsigned long arm_r2;
+ unsigned long arm_r3;
+ unsigned long arm_r4;
+ unsigned long arm_r5;
+ unsigned long arm_r6;
+ unsigned long arm_r7;
+ unsigned long arm_r8;
+ unsigned long arm_r9;
+ unsigned long arm_r10;
+ unsigned long arm_fp;
+ unsigned long arm_ip;
+ unsigned long arm_sp;
+ unsigned long arm_lr;
+ unsigned long arm_pc;
+ unsigned long arm_cpsr;
+ unsigned long fault_address;
+};
+
+/* Copied from the Linux kernel header arch/arm/include/asm/ucontext.h */
+
+#define VFP_MAGIC 0x56465001
+#define VFP_STORAGE_SIZE sizeof(struct vfp_sigframe)
+
+struct vfp_sigframe {
+ unsigned long magic;
+ unsigned long size;
+ struct user_vfp ufp;
+ struct user_vfp_exc ufp_exc;
+};
+
+typedef struct vfp_sigframe fpu_state_t;
+
+struct aux_sigframe {
+ /*
+ struct crunch_sigframe crunch;
+ struct iwmmxt_sigframe iwmmxt;
+ */
+
+ struct vfp_sigframe vfp;
+ unsigned long end_magic;
+} __attribute__((__aligned__(8)));
+
+#include "sigframe.h"
+
+struct sigframe {
+ struct rt_ucontext uc;
+ unsigned long retcode[2];
+};
+
+struct rt_sigframe {
+ struct rt_siginfo info;
+ struct sigframe sig;
+};
+
+
+#define ARCH_RT_SIGRETURN(new_sp) \
+ asm volatile( \
+ "mov %%sp, %0 \n" \
+ "mov %%r7, #"__stringify(__NR_rt_sigreturn)" \n" \
+ "svc #0 \n" \
+ : \
+ : "r"(new_sp) \
+ : "sp","memory")
+
+#define RUN_CLONE_RESTORE_FN(ret, clone_flags, new_sp, parent_tid, \
+ thread_args, clone_restore_fn) \
+ asm volatile( \
+ "clone_emul: \n" \
+ "ldr %%r1, %2 \n" \
+ "sub %%r1, #16 \n" \
+ "mov %%r0, %%%6 \n" \
+ "str %%r0, [%%r1, #4] \n" \
+ "mov %%r0, %%%5 \n" \
+ "str %%r0, [%%r1] \n" \
+ "mov %%r0, %%%1 \n" \
+ "mov %%r2, %%%3 \n" \
+ "mov %%r3, %%%4 \n" \
+ "mov %%r7, #"__stringify(__NR_clone)" \n" \
+ "svc #0 \n" \
+ \
+ "cmp %%r0, #0 \n" \
+ "beq thread_run \n" \
+ \
+ "mov %%%0, %%r0 \n" \
+ "b clone_end \n" \
+ \
+ "thread_run: \n" \
+ "pop { %%r1 } \n" \
+ "pop { %%r0 } \n" \
+ "bx %%r1 \n" \
+ \
+ "clone_end: \n" \
+ : "=r"(ret) \
+ : "r"(clone_flags), \
+ "m"(new_sp), \
+ "r"(&parent_tid), \
+ "r"(&thread_args[i].pid), \
+ "r"(clone_restore_fn), \
+ "r"(&thread_args[i]) \
+ : "r0", "r1", "r2", "r3", "r7", "memory")
+
+#define ARCH_FAIL_CORE_RESTORE \
+ asm volatile( \
+ "mov %%sp, %0 \n" \
+ "mov %%r0, #0 \n" \
+ "bx %%r0 \n" \
+ : \
+ : "r"(ret) \
+ : "memory")
+
+
+#define RT_SIGFRAME_UC(rt_sigframe) rt_sigframe->sig.uc
+#define RT_SIGFRAME_REGIP(rt_sigframe) (rt_sigframe)->sig.uc.uc_mcontext.arm_ip
+#define RT_SIGFRAME_HAS_FPU(rt_sigframe) 1
+#define RT_SIGFRAME_FPU(rt_sigframe) ((struct aux_sigframe *)&sigframe->sig.uc.uc_regspace)->vfp
+
+#define SIGFRAME_OFFSET 0
+
+
+int restore_gpregs(struct rt_sigframe *f, UserArmRegsEntry *r);
+int restore_nonsigframe_gpregs(UserArmRegsEntry *r);
+
+static inline int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe, fpu_state_t *fpu_state) { return 0; }
+
+static inline void restore_tls(tls_t *ptls) {
+ asm (
+ "mov %%r7, #15 \n"
+ "lsl %%r7, #16 \n"
+ "mov %%r0, #5 \n"
+ "add %%r7, %%r0 \n" /* r7 = 0xF005 */
+ "ldr %%r0, [%0] \n"
+ "svc #0 \n"
+ :
+ : "r"(ptls)
+ : "r0", "r7"
+ );
+}
+
+static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
+{
+ return 0;
+}
+
+static inline int ptrace_flush_breakpoints(pid_t pid)
+{
+ return 0;
+}
+
+#endif
diff --git a/arch/arm/include/asm/string.h b/arch/arm/include/asm/string.h
new file mode 100644
index 000000000..2c3a34fbb
--- /dev/null
+++ b/arch/arm/include/asm/string.h
@@ -0,0 +1,7 @@
+#ifndef __CR_ASM_STRING_H__
+#define __CR_ASM_STRING_H__
+
+#include "compiler.h"
+#include "asm-generic/string.h"
+
+#endif /* __CR_ASM_STRING_H__ */
diff --git a/arch/arm/include/asm/syscall-aux.S b/arch/arm/include/asm/syscall-aux.S
new file mode 100644
index 000000000..8bc01c3ec
--- /dev/null
+++ b/arch/arm/include/asm/syscall-aux.S
@@ -0,0 +1,13 @@
+nr_sys_mmap:
+ .long 192
+
+ENTRY(sys_mmap)
+ push {%r4, %r5, %r7, %lr}
+ ldr %r4, [%sp, #16]
+ ldr %r5, [%sp, #20]
+ lsr %r5, #12
+ adr %r7, nr_sys_mmap
+ ldr %r7, [%r7]
+ svc 0x00000000
+ pop {%r4, %r5, %r7, %pc}
+END(sys_mmap)
diff --git a/arch/arm/include/asm/syscall-aux.h b/arch/arm/include/asm/syscall-aux.h
new file mode 100644
index 000000000..ec8c2d383
--- /dev/null
+++ b/arch/arm/include/asm/syscall-aux.h
@@ -0,0 +1,8 @@
+#define __NR_mmap2 192
+
+#define __ARM_NR_BASE 0x0f0000
+#define __ARM_NR_breakpoint (__ARM_NR_BASE+1)
+#define __ARM_NR_cacheflush (__ARM_NR_BASE+2)
+#define __ARM_NR_usr26 (__ARM_NR_BASE+3)
+#define __ARM_NR_usr32 (__ARM_NR_BASE+4)
+#define __ARM_NR_set_tls (__ARM_NR_BASE+5)
diff --git a/arch/arm/include/asm/types.h b/arch/arm/include/asm/types.h
new file mode 100644
index 000000000..e9aa636dc
--- /dev/null
+++ b/arch/arm/include/asm/types.h
@@ -0,0 +1,117 @@
+#ifndef __CR_ASM_TYPES_H__
+#define __CR_ASM_TYPES_H__
+
+#include
+#include
+#include "protobuf/core.pb-c.h"
+
+#include "asm-generic/page.h"
+#include "asm/bitops.h"
+#include "asm/int.h"
+
+#define SIGMAX 64
+#define SIGMAX_OLD 31
+
+#define MAJOR(dev) ((dev)>>8)
+#define MINOR(dev) ((dev) & 0xff)
+
+typedef void rt_signalfn_t(int, siginfo_t *, void *);
+typedef rt_signalfn_t *rt_sighandler_t;
+
+typedef void rt_restorefn_t(void);
+typedef rt_restorefn_t *rt_sigrestore_t;
+
+#define _KNSIG 64
+#define _NSIG_BPW 32
+
+#define _KNSIG_WORDS (_KNSIG / _NSIG_BPW)
+
+typedef struct {
+ unsigned long sig[_KNSIG_WORDS];
+} k_rtsigset_t;
+
+static inline void ksigfillset(k_rtsigset_t *set)
+{
+ int i;
+ for (i = 0; i < _KNSIG_WORDS; i++)
+ set->sig[i] = (unsigned long)-1;
+}
+
+#define SA_RESTORER 0x04000000
+
+typedef struct {
+ rt_sighandler_t rt_sa_handler;
+ unsigned long rt_sa_flags;
+ rt_sigrestore_t rt_sa_restorer;
+ k_rtsigset_t rt_sa_mask;
+} rt_sigaction_t;
+
+/*
+ * Copied from the Linux kernel header arch/arm/include/asm/ptrace.h
+ *
+ * A thread ARM CPU context
+ */
+
+typedef struct {
+ long uregs[18];
+} user_regs_struct_t;
+
+#define ARM_cpsr uregs[16]
+#define ARM_pc uregs[15]
+#define ARM_lr uregs[14]
+#define ARM_sp uregs[13]
+#define ARM_ip uregs[12]
+#define ARM_fp uregs[11]
+#define ARM_r10 uregs[10]
+#define ARM_r9 uregs[9]
+#define ARM_r8 uregs[8]
+#define ARM_r7 uregs[7]
+#define ARM_r6 uregs[6]
+#define ARM_r5 uregs[5]
+#define ARM_r4 uregs[4]
+#define ARM_r3 uregs[3]
+#define ARM_r2 uregs[2]
+#define ARM_r1 uregs[1]
+#define ARM_r0 uregs[0]
+#define ARM_ORIG_r0 uregs[17]
+
+
+/* Copied from arch/arm/include/asm/user.h */
+
+struct user_vfp {
+ unsigned long long fpregs[32];
+ unsigned long fpscr;
+};
+
+struct user_vfp_exc {
+ unsigned long fpexc;
+ unsigned long fpinst;
+ unsigned long fpinst2;
+};
+
+#define ASSIGN_TYPED(a, b) do { a = (typeof(a))b; } while (0)
+#define ASSIGN_MEMBER(a,b,m) do { ASSIGN_TYPED((a)->m, (b)->m); } while (0)
+
+#define REG_RES(regs) ((regs).ARM_r0)
+#define REG_IP(regs) ((regs).ARM_pc)
+#define REG_SYSCALL_NR(regs) ((regs).ARM_r7)
+
+#define TASK_SIZE 0xbf000000
+
+#define AT_VECTOR_SIZE 40
+
+typedef UserArmRegsEntry UserRegsEntry;
+
+#define CORE_ENTRY__MARCH CORE_ENTRY__MARCH__ARM
+
+#define CORE_THREAD_ARCH_INFO(core) core->ti_arm
+
+#define TI_SP(core) ((core)->ti_arm->gpregs->sp)
+
+typedef u32 auxv_t;
+typedef u32 tls_t;
+
+static inline void *decode_pointer(u64 v) { return (void*)(u32)v; }
+static inline u64 encode_pointer(void *p) { return (u32)p; }
+
+#endif /* __CR_ASM_TYPES_H__ */
diff --git a/arch/arm/parasite-head.S b/arch/arm/parasite-head.S
new file mode 100644
index 000000000..b15fcbae2
--- /dev/null
+++ b/arch/arm/parasite-head.S
@@ -0,0 +1,23 @@
+#include "asm/linkage.h"
+#include "parasite.h"
+
+ .section .head.text, "ax"
+ENTRY(__export_parasite_head_start)
+ sub %r2, %pc, #8 @ get the address of this instruction
+
+ adr %r0, __export_parasite_cmd
+ ldr %r0, [%r0]
+
+ adr %r1, parasite_args_ptr
+ ldr %r1, [%r1]
+ add %r1, %r1, %r2 @ fixup __export_parasite_args
+
+ bl parasite_service
+ .byte 0xf0, 0x01, 0xf0, 0xe7 @ the instruction UDF #32 generates the signal SIGTRAP in Linux
+
+parasite_args_ptr:
+ .long __export_parasite_args
+
+__export_parasite_cmd:
+ .long 0
+END(__export_parasite_head_start)
diff --git a/arch/arm/restorer.c b/arch/arm/restorer.c
new file mode 100644
index 000000000..786feeeb3
--- /dev/null
+++ b/arch/arm/restorer.c
@@ -0,0 +1,15 @@
+#include
+
+#include "restorer.h"
+#include "asm/restorer.h"
+#include "asm/string.h"
+
+#include "syscall.h"
+#include "log.h"
+#include "asm/fpu.h"
+#include "cpu.h"
+
+int restore_nonsigframe_gpregs(UserArmRegsEntry *r)
+{
+ return 0;
+}
diff --git a/arch/arm/syscall-common.S b/arch/arm/syscall-common.S
new file mode 100644
index 000000000..c3cbf7105
--- /dev/null
+++ b/arch/arm/syscall-common.S
@@ -0,0 +1,34 @@
+#include "asm/linkage.h"
+
+@ We use the register R8 unlike libc that uses R12.
+@ This avoids corruption of the register by the stub
+@ for the syscall sys_munmap() when syscalls are hooked
+@ by ptrace(). However we have to make sure that
+@ the compiler doesn't use the register on the route
+@ between parasite_service() and sys_munmap().
+
+syscall_common:
+ ldr %r7, [%r7]
+ add %r8, %sp, #24
+ ldm %r8, {%r4, %r5, %r6}
+ svc 0x00000000
+ pop {%r4, %r5, %r6, %r7, %r8, %pc}
+
+
+.macro syscall name, nr
+ .nr_\name :
+ .long \nr
+
+ ENTRY(\name)
+ push {%r4, %r5, %r6, %r7, %r8, %lr}
+ adr %r7, .nr_\name
+ b syscall_common
+ END(\name)
+.endm
+
+
+ENTRY(__cr_restore_rt)
+ adr %r7, .nr_sys_rt_sigreturn
+ ldr %r7, [%r7]
+ svc #0
+END(__cr_restore_rt)
diff --git a/arch/arm/syscall.def b/arch/arm/syscall.def
new file mode 100644
index 000000000..64321b62a
--- /dev/null
+++ b/arch/arm/syscall.def
@@ -0,0 +1,103 @@
+#
+# System calls table, please make sure the table consist only the syscalls
+# really used somewhere in project.
+#
+# The template is (name and arguments are optinal if you need only __NR_x
+# defined, but no realy entry point in syscalls lib).
+#
+# name/alias code64 code32 arguments
+# -----------------------------------------------------------------------
+#
+read 63 3 (int fd, void *buf, unsigned long count)
+write 64 4 (int fd, const void *buf, unsigned long count)
+open ! 5 (const char *filename, unsigned long flags, unsigned long mode)
+close 57 6 (int fd)
+lseek 62 19 (int fd, unsigned long offset, unsigned long origin)
+mmap 222 ! (void *addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long offset)
+mprotect 226 125 (const void *addr, unsigned long len, unsigned long prot)
+munmap 215 91 (void *addr, unsigned long len)
+brk 214 45 (void *addr)
+rt_sigaction sigaction 134 174 (int signum, const rt_sigaction_t *act, rt_sigaction_t *oldact, size_t sigsetsize)
+rt_sigprocmask sigprocmask 135 175 (int how, k_rtsigset_t *set, k_rtsigset_t *old, size_t sigsetsize)
+rt_sigreturn 139 173 (void)
+ioctl 29 54 (unsigned int fd, unsigned int cmd, unsigned long arg)
+pread64 67 180 (unsigned int fd, char *buf, size_t count, loff_t pos)
+mremap 216 163 (unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flag, unsigned long new_addr)
+mincore 232 219 (void *addr, unsigned long size, unsigned char *vec)
+madvise 233 220 (unsigned long start, size_t len, int behavior)
+shmat 196 305 (int shmid, void *shmaddr, int shmflag)
+pause 1061 29 (void)
+nanosleep 101 162 (struct timespec *req, struct timespec *rem)
+getitimer 102 105 (int which, const struct itimerval *val)
+setitimer 103 104 (int which, const struct itimerval *val, struct itimerval *old)
+getpid 172 20 (void)
+socket 198 281 (int domain, int type, int protocol)
+connect 203 283 (int sockfd, struct sockaddr *addr, int addrlen)
+sendto 206 290 (int sockfd, void *buff, size_t len, unsigned int flags, struct sockaddr *addr, int addr_len)
+recvfrom 207 292 (int sockfd, void *ubuf, size_t size, unsigned int flags, struct sockaddr *addr, int *addr_len)
+sendmsg 211 296 (int sockfd, const struct msghdr *msg, int flags)
+recvmsg 212 297 (int sockfd, struct msghdr *msg, int flags)
+shutdown 210 293 (int sockfd, int how)
+bind 235 282 (int sockfd, const struct sockaddr *addr, int addrlen)
+setsockopt 208 294 (int sockfd, int level, int optname, const void *optval, socklen_t optlen)
+getsockopt 209 295 (int sockfd, int level, int optname, const void *optval, socklen_t *optlen)
+clone 220 120 (unsigned long flags, void *child_stack, void *parent_tid, void *child_tid)
+exit 93 1 (unsigned long error_code)
+wait4 260 114 (int pid, int *status, int options, struct rusage *ru)
+kill 129 37 (long pid, int sig)
+fcntl 25 55 (int fd, int type, long arg)
+flock 32 143 (int fd, unsigned long cmd)
+mkdir ! 39 (const char *name, int mode)
+rmdir ! 40 (const char *name)
+unlink ! 10 (char *pathname)
+readlink 78 85 (const char *path, char *buf, int bufsize)
+umask 166 60 (int mask)
+getgroups 158 205 (int gsize, unsigned int *groups)
+setresuid 147 164 (int uid, int euid, int suid)
+getresuid 148 165 (int *uid, int *euid, int *suid)
+setresgid 149 170 (int gid, int egid, int sgid)
+getresgid 150 171 (int *gid, int *egid, int *sgid)
+getpgid 155 132 (pid_t pid)
+setfsuid 151 138 (int fsuid)
+setfsgid 152 139 (int fsgid)
+getsid 156 147 (void)
+capget 90 184 (struct cap_header *h, struct cap_data *d)
+capset 91 185 (struct cap_header *h, struct cap_data *d)
+rt_sigqueueinfo 138 178 (pid_t pid, int sig, siginfo_t *info)
+setpriority 140 97 (int which, int who, int nice)
+sched_setscheduler 119 156 (int pid, int policy, struct sched_param *p)
+sigaltstack 132 186 (const void *uss, void *uoss)
+personality 92 136 (unsigned int personality)
+prctl 167 172 (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
+arch_prctl ! 17 (int option, unsigned long addr)
+setrlimit 164 75 (int resource, struct krlimit *rlim)
+mount 40 21 (char *dev_nmae, char *dir_name, char *type, unsigned long flags, void *data)
+umount2 39 52 (char *name, int flags)
+gettid 178 224 (void)
+futex 98 240 (u32 *uaddr, int op, u32 val, struct timespec *utime, u32 *uaddr2, u32 val3)
+set_tid_address 96 256 (int *tid_addr)
+restart_syscall 128 0 (void)
+timer_create 107 257 (clockid_t which_clock, struct sigevent *timer_event_spec, timer_t *created_timer_id)
+timer_settime 110 258 (timer_t timer_id, int flags, const struct itimerspec *new_setting, struct itimerspec *old_setting)
+timer_gettime 108 259 (int timer_id, const struct itimerspec *setting)
+timer_getoverrun 109 260 (int timer_id)
+timer_delete 111 261 (timer_t timer_id)
+clock_gettime 113 263 (const clockid_t which_clock, const struct timespec *tp)
+exit_group 94 248 (int error_code)
+set_robust_list 99 338 (struct robust_list_head *head, size_t len)
+get_robust_list 100 339 (int pid, struct robust_list_head **head_ptr, size_t *len_ptr)
+signalfd4 74 355 (int fd, k_rtsigset_t *mask, size_t sizemask, int flags)
+rt_tgsigqueueinfo 240 363 (pid_t tgid, pid_t pid, int sig, siginfo_t *info)
+vmsplice 75 343 (int fd, const struct iovec *iov, unsigned long nr_segs, unsigned int flags)
+timerfd_settime 86 353 (int ufd, int flags, const struct itimerspec *utmr, struct itimerspec *otmr)
+fanotify_init 262 367 (unsigned int flags, unsigned int event_f_flags)
+fanotify_mark 263 368 (int fanotify_fd, unsigned int flags, u64 mask, int dfd, const char *pathname)
+open_by_handle_at 265 371 (int mountdirfd, struct file_handle *handle, int flags)
+setns 268 375 (int fd, int nstype)
+kcmp 272 378 (pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2)
+openat 56 322 (int dirfd, const char *pathname, int flags, mode_t mode)
+mkdirat 34 323 (int dirfd, const char *pathname, mode_t mode)
+unlinkat 35 328 (int dirfd, const char *pathname, int flags)
+memfd_create 279 385 (const char *name, unsigned int flags)
+io_setup 0 243 (unsigned nr_events, aio_context_t *ctx)
+io_getevents 4 245 (aio_context_t ctx, long min_nr, long nr, struct io_event *evs, struct timespec *tmo)
diff --git a/arch/arm/uidiv.S b/arch/arm/uidiv.S
new file mode 100644
index 000000000..e77f6100c
--- /dev/null
+++ b/arch/arm/uidiv.S
@@ -0,0 +1,186 @@
+.globl __aeabi_uidiv
+
+work .req r4 @ XXXX is this safe ?
+dividend .req r0
+divisor .req r1
+overdone .req r2
+result .req r2
+curbit .req r3
+
+#define LSYM(x) x
+
+.macro THUMB_DIV_MOD_BODY modulo
+ @ Load the constant 0x10000000 into our work register.
+ mov work, #1
+ lsl work, #28
+LSYM(Loop1):
+ @ Unless the divisor is very big, shift it up in multiples of
+ @ four bits, since this is the amount of unwinding in the main
+ @ division loop. Continue shifting until the divisor is
+ @ larger than the dividend.
+ cmp divisor, work
+ bhs LSYM(Lbignum)
+ cmp divisor, dividend
+ bhs LSYM(Lbignum)
+ lsl divisor, #4
+ lsl curbit, #4
+ b LSYM(Loop1)
+LSYM(Lbignum):
+ @ Set work to 0x80000000
+ lsl work, #3
+LSYM(Loop2):
+ @ For very big divisors, we must shift it a bit at a time, or
+ @ we will be in danger of overflowing.
+ cmp divisor, work
+ bhs LSYM(Loop3)
+ cmp divisor, dividend
+ bhs LSYM(Loop3)
+ lsl divisor, #1
+ lsl curbit, #1
+ b LSYM(Loop2)
+LSYM(Loop3):
+ @ Test for possible subtractions ...
+ .if \modulo
+ @ ... On the final pass, this may subtract too much from the dividend,
+ @ so keep track of which subtractions are done, we can fix them up
+ @ afterwards.
+ mov overdone, #0
+ cmp dividend, divisor
+ blo LSYM(Lover1)
+ sub dividend, dividend, divisor
+LSYM(Lover1):
+ lsr work, divisor, #1
+ cmp dividend, work
+ blo LSYM(Lover2)
+ sub dividend, dividend, work
+ mov ip, curbit
+ mov work, #1
+ ror curbit, work
+ orr overdone, curbit
+ mov curbit, ip
+LSYM(Lover2):
+ lsr work, divisor, #2
+ cmp dividend, work
+ blo LSYM(Lover3)
+ sub dividend, dividend, work
+ mov ip, curbit
+ mov work, #2
+ ror curbit, work
+ orr overdone, curbit
+ mov curbit, ip
+LSYM(Lover3):
+ lsr work, divisor, #3
+ cmp dividend, work
+ blo LSYM(Lover4)
+ sub dividend, dividend, work
+ mov ip, curbit
+ mov work, #3
+ ror curbit, work
+ orr overdone, curbit
+ mov curbit, ip
+LSYM(Lover4):
+ mov ip, curbit
+ .else
+ @ ... and note which bits are done in the result. On the final pass,
+ @ this may subtract too much from the dividend, but the result will be ok,
+ @ since the "bit" will have been shifted out at the bottom.
+ cmp dividend, divisor
+ blo LSYM(Lover1)
+ sub dividend, dividend, divisor
+ orr result, result, curbit
+LSYM(Lover1):
+ lsr work, divisor, #1
+ cmp dividend, work
+ blo LSYM(Lover2)
+ sub dividend, dividend, work
+ lsr work, curbit, #1
+ orr result, work
+LSYM(Lover2):
+ lsr work, divisor, #2
+ cmp dividend, work
+ blo LSYM(Lover3)
+ sub dividend, dividend, work
+ lsr work, curbit, #2
+ orr result, work
+LSYM(Lover3):
+ lsr work, divisor, #3
+ cmp dividend, work
+ blo LSYM(Lover4)
+ sub dividend, dividend, work
+ lsr work, curbit, #3
+ orr result, work
+LSYM(Lover4):
+ .endif
+
+ cmp dividend, #0 @ Early termination?
+ beq LSYM(Lover5)
+ lsr curbit, #4 @ No, any more bits to do?
+ beq LSYM(Lover5)
+ lsr divisor, #4
+ b LSYM(Loop3)
+LSYM(Lover5):
+ .if \modulo
+ @ Any subtractions that we should not have done will be recorded in
+ @ the top three bits of "overdone". Exactly which were not needed
+ @ are governed by the position of the bit, stored in ip.
+ mov work, #0xe
+ lsl work, #28
+ and overdone, work
+ beq LSYM(Lgot_result)
+
+ @ If we terminated early, because dividend became zero, then the
+ @ bit in ip will not be in the bottom nibble, and we should not
+ @ perform the additions below. We must test for this though
+ @ (rather relying upon the TSTs to prevent the additions) since
+ @ the bit in ip could be in the top two bits which might then match
+ @ with one of the smaller RORs.
+ mov curbit, ip
+ mov work, #0x7
+ tst curbit, work
+ beq LSYM(Lgot_result)
+
+ mov curbit, ip
+ mov work, #3
+ ror curbit, work
+ tst overdone, curbit
+ beq LSYM(Lover6)
+ lsr work, divisor, #3
+ add dividend, work
+LSYM(Lover6):
+ mov curbit, ip
+ mov work, #2
+ ror curbit, work
+ tst overdone, curbit
+ beq LSYM(Lover7)
+ lsr work, divisor, #2
+ add dividend, work
+LSYM(Lover7):
+ mov curbit, ip
+ mov work, #1
+ ror curbit, work
+ tst overdone, curbit
+ beq LSYM(Lgot_result)
+ lsr work, divisor, #1
+ add dividend, work
+ .endif
+LSYM(Lgot_result):
+.endm
+
+
+ .thumb
+ .text
+
+__aeabi_uidiv:
+ mov curbit, #1
+ mov result, #0
+
+ push { work }
+ cmp dividend, divisor
+ blo LSYM(Lgot_result)
+
+ THUMB_DIV_MOD_BODY 0
+
+ mov r0, result
+ pop { work }
+
+ bx lr
diff --git a/arch/scripts/arm/gen-sys-exec-tbl.pl b/arch/scripts/arm/gen-sys-exec-tbl.pl
new file mode 100755
index 000000000..a3037b78c
--- /dev/null
+++ b/arch/scripts/arm/gen-sys-exec-tbl.pl
@@ -0,0 +1,39 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my $in = $ARGV[0];
+my $tblout = $ARGV[1];
+my $bits = $ARGV[2];
+
+my $code = "code$bits";
+
+open TBLOUT, ">", $tblout or die $!;
+open IN, "<", $in or die $!;
+
+print TBLOUT "/* Autogenerated, don't edit */\n";
+
+for () {
+ if ($_ =~ /\#/) {
+ next;
+ }
+
+ my $sys_name;
+ my $sys_num;
+
+ if (/(?\S+)\s+(?\S+)\s+(?\d+|\!)\s+(?(?:\d+|\!))\s+\((?.+)\)/) {
+ $sys_name = $+{alias};
+ } elsif (/(?\S+)\s+(?\d+|\!)\s+(?(?:\d+|\!))\s+\((?.+)\)/) {
+ $sys_name = $+{name};
+ } else {
+ unlink $tblout;
+ die "Invalid syscall definition file: invalid entry $_\n";
+ }
+
+ $sys_num = $+{$code};
+
+ if ($sys_num ne "!") {
+ print TBLOUT "SYSCALL($sys_name, $sys_num)\n";
+ }
+}
diff --git a/arch/scripts/arm/gen-syscalls.pl b/arch/scripts/arm/gen-syscalls.pl
new file mode 100755
index 000000000..6fb8f3bf2
--- /dev/null
+++ b/arch/scripts/arm/gen-syscalls.pl
@@ -0,0 +1,95 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+my $in = $ARGV[0];
+my $codesout = $ARGV[1];
+my $codes = $ARGV[1];
+$codes =~ s/.*include\///g;
+my $protosout = $ARGV[2];
+my $protos = $ARGV[2];
+$protos =~ s/.*include\///g;
+my $asmout = $ARGV[3];
+my $asmcommon = $ARGV[4];
+my $prototypes = $ARGV[5];
+$prototypes =~ s/.*include\///g;
+my $bits = $ARGV[6];
+
+my $codesdef = $codes;
+$codesdef =~ tr/.-/_/;
+my $protosdef = $protos;
+$protosdef =~ tr/.-/_/;
+my $code = "code$bits";
+my $need_aux = 0;
+
+unlink $codesout;
+unlink $protosout;
+unlink $asmout;
+
+open CODESOUT, ">", $codesout or die $!;
+open PROTOSOUT, ">", $protosout or die $!;
+open ASMOUT, ">", $asmout or die $!;
+open IN, "<", $in or die $!;
+
+print CODESOUT <<"END";
+/* Autogenerated, don't edit */
+#ifndef $codesdef
+#define $codesdef
+END
+
+print PROTOSOUT <<"END";
+/* Autogenerated, don't edit */
+#ifndef $protosdef
+#define $protosdef
+#include "$prototypes"
+#include "$codes"
+END
+
+print ASMOUT <<"END";
+/* Autogenerated, don't edit */
+#include "$codes"
+#include "$asmcommon"
+END
+
+
+for () {
+ if ($_ =~ /\#/) {
+ next;
+ }
+
+ my $code_macro;
+ my $sys_name;
+
+ if (/(?\S+)\s+(?\S+)\s+(?\d+|\!)\s+(?(?:\d+|\!))\s+\((?.+)\)/) {
+ $code_macro = "__NR_$+{name}";
+ $sys_name = "sys_$+{alias}";
+ } elsif (/(?\S+)\s+(?\d+|\!)\s+(?(?:\d+|\!))\s+\((?.+)\)/) {
+ $code_macro = "__NR_$+{name}";
+ $sys_name = "sys_$+{name}";
+ } else {
+ unlink $codesout;
+ unlink $protosout;
+ unlink $asmout;
+
+ die "Invalid syscall definition file: invalid entry $_\n";
+ }
+
+ if ($+{$code} ne "!") {
+ print CODESOUT "#define $code_macro $+{$code}\n";
+ print ASMOUT "syscall $sys_name, $code_macro\n";
+
+ } else {
+ $need_aux = 1;
+ }
+
+ print PROTOSOUT "extern long $sys_name($+{args});\n";
+}
+
+if ($need_aux == 1) {
+ print ASMOUT "#include \"asm/syscall-aux.S\"\n";
+ print CODESOUT "#include \"asm/syscall-aux.h\"\n";
+}
+
+print CODESOUT "#endif /* $codesdef */";
+print PROTOSOUT "#endif /* $protosdef */";
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
new file mode 100644
index 000000000..71c748bfc
--- /dev/null
+++ b/arch/x86/Makefile
@@ -0,0 +1,56 @@
+targets += syscalls
+targets += crtools
+
+SYS-ASM := syscalls.S
+
+syscalls-asm-y += $(SYS-ASM:.S=).o
+crtools-obj-y += crtools.o
+crtools-obj-y += cpu.o
+crtools-obj-y += prlimit.o
+
+SYS-DEF := syscall-x86-64.def
+SYS-ASM-COMMON := syscall-common-x86-64.S
+
+SYS-TYPES := include/syscall-types.h
+SYS-CODES := include/syscall-codes.h
+SYS-PROTO := include/syscall.h
+
+SYS-GEN := syscalls-x86-64.sh
+
+SYS-EXEC-TBL := sys-exec-tbl.c
+
+syscalls-asm-y-asmflags := -fpie -Wstrict-prototypes -Wa,--noexecstack
+syscalls-asm-y-asmflags += -nostdlib -fomit-frame-pointer -I$(obj)
+
+ASMFLAGS += -D__ASSEMBLY__
+
+$(obj)/$(SYS-ASM): $(obj)/$(SYS-GEN) $(obj)/$(SYS-DEF) $(obj)/$(SYS-ASM-COMMON) $(SYS-TYPES)
+ $(E) " GEN " $@
+ $(Q) $(SH) \
+ $(obj)/$(SYS-GEN) --asm \
+ $(obj)/$(SYS-DEF) \
+ $(SYS-CODES) \
+ $(SYS-PROTO) \
+ $(obj)/$(SYS-ASM) \
+ $(SYS-ASM-COMMON) \
+ $(SYS-TYPES)
+
+$(obj)/syscalls.o: $(obj)/$(SYS-ASM)
+
+$(obj)/$(SYS-EXEC-TBL): $(obj)/$(SYS-GEN) $(obj)/$(SYS-DEF)
+ $(E) " GEN " $@
+ $(Q) $(SH) \
+ $(obj)/$(SYS-GEN) --exec \
+ $(obj)/$(SYS-DEF) \
+ $(obj)/$(SYS-EXEC-TBL)
+
+_all += $(obj)/$(SYS-EXEC-TBL)
+
+cleanup-y += $(obj)/$(SYS-EXEC-TBL) $(obj)/$(SYS-ASM)
+cleanup-y += $(SYS-CODES)
+cleanup-y += $(SYS-PROTO)
+
+ifneq ($(MAKECMDGOALS),clean)
+deps-after := $(obj)/$(SYS-ASM)
+incdeps := y
+endif
diff --git a/arch/x86/cpu.c b/arch/x86/cpu.c
new file mode 100644
index 000000000..9fcba7c4f
--- /dev/null
+++ b/arch/x86/cpu.c
@@ -0,0 +1,491 @@
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "asm/bitops.h"
+#include "asm/types.h"
+#include "asm/cpu.h"
+#include "asm/fpu.h"
+
+#include "compiler.h"
+
+#include "cr_options.h"
+#include "proc_parse.h"
+#include "util.h"
+#include "log.h"
+
+#include "cpu.h"
+
+#include "protobuf.h"
+#include "protobuf/cpuinfo.pb-c.h"
+
+#undef LOG_PREFIX
+#define LOG_PREFIX "cpu: "
+
+static struct cpuinfo_x86 rt_cpu_info;
+
+static void set_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
+{
+ if (likely(feature < NCAPINTS_BITS))
+ set_bit(feature, (unsigned long *)c->x86_capability);
+}
+
+static void clear_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
+{
+ if (likely(feature < NCAPINTS_BITS))
+ clear_bit(feature, (unsigned long *)c->x86_capability);
+}
+
+static int test_cpu_cap(struct cpuinfo_x86 *c, unsigned int feature)
+{
+ if (likely(feature < NCAPINTS_BITS))
+ return test_bit(feature, (unsigned long *)c->x86_capability);
+ return 0;
+}
+
+bool cpu_has_feature(unsigned int feature)
+{
+ return test_cpu_cap(&rt_cpu_info, feature);
+}
+
+static int cpu_init_cpuid(struct cpuinfo_x86 *c)
+{
+ /*
+ * See cpu_detect() in the kernel, also
+ * read cpuid specs not only from general
+ * SDM but for extended instructions set
+ * reference.
+ */
+
+ /* Get vendor name */
+ cpuid(0x00000000,
+ (unsigned int *)&c->cpuid_level,
+ (unsigned int *)&c->x86_vendor_id[0],
+ (unsigned int *)&c->x86_vendor_id[8],
+ (unsigned int *)&c->x86_vendor_id[4]);
+
+ if (!strcmp(c->x86_vendor_id, "GenuineIntel")) {
+ c->x86_vendor = X86_VENDOR_INTEL;
+ } else if (!strcmp(c->x86_vendor_id, "AuthenticAMD")) {
+ c->x86_vendor = X86_VENDOR_AMD;
+ } else {
+ pr_err("Unsupported CPU vendor %s\n",
+ c->x86_vendor_id);
+ return -1;
+ }
+
+ c->x86_family = 4;
+
+ /* Intel-defined flags: level 0x00000001 */
+ if (c->cpuid_level >= 0x00000001) {
+ u32 eax, ebx, ecx, edx;
+
+ cpuid(0x00000001, &eax, &ebx, &ecx, &edx);
+ c->x86_family = (eax >> 8) & 0xf;
+ c->x86_model = (eax >> 4) & 0xf;
+ c->x86_mask = eax & 0xf;
+
+ if (c->x86_family == 0xf)
+ c->x86_family += (eax >> 20) & 0xff;
+ if (c->x86_family >= 0x6)
+ c->x86_model += ((eax >> 16) & 0xf) << 4;
+
+ c->x86_capability[0] = edx;
+ c->x86_capability[4] = ecx;
+ }
+
+ /* Additional Intel-defined flags: level 0x00000007 */
+ if (c->cpuid_level >= 0x00000007) {
+ u32 eax, ebx, ecx, edx;
+
+ cpuid_count(0x00000007, 0, &eax, &ebx, &ecx, &edx);
+ c->x86_capability[9] = ebx;
+ c->x86_capability[11] = ecx;
+ }
+
+ /* Extended state features: level 0x0000000d */
+ if (c->cpuid_level >= 0x0000000d) {
+ u32 eax, ebx, ecx, edx;
+
+ cpuid_count(0x0000000d, 1, &eax, &ebx, &ecx, &edx);
+ c->x86_capability[10] = eax;
+ }
+
+ /* AMD-defined flags: level 0x80000001 */
+ c->extended_cpuid_level = cpuid_eax(0x80000000);
+
+ if ((c->extended_cpuid_level & 0xffff0000) == 0x80000000) {
+ if (c->extended_cpuid_level >= 0x80000001) {
+ c->x86_capability[1] = cpuid_edx(0x80000001);
+ c->x86_capability[6] = cpuid_ecx(0x80000001);
+ }
+ }
+
+ /*
+ * We're don't care about scattered features for now,
+ * otherwise look into init_scattered_cpuid_features()
+ * in kernel.
+ */
+
+ if (c->extended_cpuid_level >= 0x80000004) {
+ unsigned int *v;
+ char *p, *q;
+ v = (unsigned int *)c->x86_model_id;
+ cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
+ cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
+ cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
+ c->x86_model_id[48] = 0;
+
+ /*
+ * Intel chips right-justify this string for some dumb reason;
+ * undo that brain damage:
+ */
+ p = q = &c->x86_model_id[0];
+ while (*p == ' ')
+ p++;
+ if (p != q) {
+ while (*p)
+ *q++ = *p++;
+ while (q <= &c->x86_model_id[48])
+ *q++ = '\0'; /* Zero-pad the rest */
+ }
+ }
+
+ /* On x86-64 NOP is always present */
+ set_cpu_cap(c, X86_FEATURE_NOPL);
+
+ switch (c->x86_vendor) {
+ case X86_VENDOR_INTEL:
+ /*
+ * Strictly speaking we need to read MSR_IA32_MISC_ENABLE
+ * here but on ring3 it's impossible.
+ */
+ if (c->x86_family == 15) {
+ clear_cpu_cap(c, X86_FEATURE_REP_GOOD);
+ clear_cpu_cap(c, X86_FEATURE_ERMS);
+ } else if (c->x86_family == 6) {
+ /* On x86-64 rep is fine */
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+ }
+
+ /* See filter_cpuid_features in kernel */
+ if ((s32)c->cpuid_level < (s32)0x0000000d)
+ clear_cpu_cap(c, X86_FEATURE_XSAVE);
+ break;
+ case X86_VENDOR_AMD:
+ /*
+ * Bit 31 in normal CPUID used for nonstandard 3DNow ID;
+ * 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway
+ */
+ clear_cpu_cap(c, 0 * 32 + 31);
+ if (c->x86_family >= 0x10)
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+ if (c->x86_family == 0xf) {
+ u32 level;
+
+ /* On C+ stepping K8 rep microcode works well for copy/memset */
+ level = cpuid_eax(1);
+ if ((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58)
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int cpu_init(void)
+{
+ if (cpu_init_cpuid(&rt_cpu_info))
+ return -1;
+
+ BUILD_BUG_ON(sizeof(struct xsave_struct) != XSAVE_SIZE);
+ BUILD_BUG_ON(sizeof(struct i387_fxsave_struct) != FXSAVE_SIZE);
+
+ /*
+ * Make sure that at least FPU is onboard
+ * and fxsave is supported.
+ */
+ if (cpu_has_feature(X86_FEATURE_FPU)) {
+ if (!cpu_has_feature(X86_FEATURE_FXSR)) {
+ pr_err("missing support fxsave/restore insns\n");
+ return -1;
+ }
+ }
+
+ pr_debug("fpu:%d fxsr:%d xsave:%d\n",
+ !!cpu_has_feature(X86_FEATURE_FPU),
+ !!cpu_has_feature(X86_FEATURE_FXSR),
+ !!cpu_has_feature(X86_FEATURE_XSAVE));
+
+ return 0;
+}
+
+int cpu_dump_cpuinfo(void)
+{
+ CpuinfoEntry cpu_info = CPUINFO_ENTRY__INIT;
+ CpuinfoX86Entry cpu_x86_info = CPUINFO_X86_ENTRY__INIT;
+ CpuinfoX86Entry *cpu_x86_info_ptr = &cpu_x86_info;
+ struct cr_img *img;
+
+ img = open_image(CR_FD_CPUINFO, O_DUMP);
+ if (!img)
+ return -1;
+
+ cpu_info.x86_entry = &cpu_x86_info_ptr;
+ cpu_info.n_x86_entry = 1;
+
+ cpu_x86_info.vendor_id = (rt_cpu_info.x86_vendor == X86_VENDOR_INTEL) ?
+ CPUINFO_X86_ENTRY__VENDOR__INTEL :
+ CPUINFO_X86_ENTRY__VENDOR__AMD;
+ cpu_x86_info.cpu_family = rt_cpu_info.x86_family;
+ cpu_x86_info.model = rt_cpu_info.x86_model;
+ cpu_x86_info.stepping = rt_cpu_info.x86_mask;
+ cpu_x86_info.capability_ver = 1;
+ cpu_x86_info.n_capability = ARRAY_SIZE(rt_cpu_info.x86_capability);
+ cpu_x86_info.capability = (void *)rt_cpu_info.x86_capability;
+
+ if (rt_cpu_info.x86_model_id[0])
+ cpu_x86_info.model_id = rt_cpu_info.x86_model_id;
+
+ if (pb_write_one(img, &cpu_info, PB_CPUINFO) < 0) {
+ close_image(img);
+ return -1;
+ }
+
+ close_image(img);
+ return 0;
+}
+
+#define __ins_bit(__l, __v) (1u << ((__v) - 32u * (__l)))
+
+static u32 x86_ins_capability_mask[NCAPINTS] = {
+ [0] =
+ __ins_bit(0, X86_FEATURE_FPU) |
+ __ins_bit(0, X86_FEATURE_TSC) |
+ __ins_bit(0, X86_FEATURE_CX8) |
+ __ins_bit(0, X86_FEATURE_SEP) |
+ __ins_bit(0, X86_FEATURE_CMOV) |
+ __ins_bit(0, X86_FEATURE_CLFLUSH) |
+ __ins_bit(0, X86_FEATURE_MMX) |
+ __ins_bit(0, X86_FEATURE_FXSR) |
+ __ins_bit(0, X86_FEATURE_XMM) |
+ __ins_bit(0, X86_FEATURE_XMM2),
+
+ [1] =
+ __ins_bit(1, X86_FEATURE_SYSCALL) |
+ __ins_bit(1, X86_FEATURE_MMXEXT) |
+ __ins_bit(1, X86_FEATURE_RDTSCP) |
+ __ins_bit(1, X86_FEATURE_3DNOWEXT) |
+ __ins_bit(1, X86_FEATURE_3DNOW),
+
+ [3] =
+ __ins_bit(3, X86_FEATURE_REP_GOOD) |
+ __ins_bit(3, X86_FEATURE_NOPL),
+
+ [4] =
+ __ins_bit(4, X86_FEATURE_XMM3) |
+ __ins_bit(4, X86_FEATURE_PCLMULQDQ) |
+ __ins_bit(4, X86_FEATURE_MWAIT) |
+ __ins_bit(4, X86_FEATURE_SSSE3) |
+ __ins_bit(4, X86_FEATURE_CX16) |
+ __ins_bit(4, X86_FEATURE_XMM4_1) |
+ __ins_bit(4, X86_FEATURE_XMM4_2) |
+ __ins_bit(4, X86_FEATURE_MOVBE) |
+ __ins_bit(4, X86_FEATURE_POPCNT) |
+ __ins_bit(4, X86_FEATURE_AES) |
+ __ins_bit(4, X86_FEATURE_XSAVE) |
+ __ins_bit(4, X86_FEATURE_OSXSAVE) |
+ __ins_bit(4, X86_FEATURE_AVX) |
+ __ins_bit(4, X86_FEATURE_F16C) |
+ __ins_bit(4, X86_FEATURE_RDRAND),
+
+ [6] =
+ __ins_bit(6, X86_FEATURE_ABM) |
+ __ins_bit(6, X86_FEATURE_SSE4A) |
+ __ins_bit(6, X86_FEATURE_MISALIGNSSE) |
+ __ins_bit(6, X86_FEATURE_3DNOWPREFETCH) |
+ __ins_bit(6, X86_FEATURE_XOP) |
+ __ins_bit(6, X86_FEATURE_FMA4) |
+ __ins_bit(6, X86_FEATURE_TBM),
+
+ [9] =
+ __ins_bit(9, X86_FEATURE_FSGSBASE) |
+ __ins_bit(9, X86_FEATURE_BMI1) |
+ __ins_bit(9, X86_FEATURE_HLE) |
+ __ins_bit(9, X86_FEATURE_AVX2) |
+ __ins_bit(9, X86_FEATURE_BMI2) |
+ __ins_bit(9, X86_FEATURE_ERMS) |
+ __ins_bit(9, X86_FEATURE_RTM) |
+ __ins_bit(9, X86_FEATURE_MPX) |
+ __ins_bit(9, X86_FEATURE_AVX512F) |
+ __ins_bit(9, X86_FEATURE_AVX512DQ) |
+ __ins_bit(9, X86_FEATURE_RDSEED) |
+ __ins_bit(9, X86_FEATURE_ADX) |
+ __ins_bit(9, X86_FEATURE_CLFLUSHOPT) |
+ __ins_bit(9, X86_FEATURE_AVX512PF) |
+ __ins_bit(9, X86_FEATURE_AVX512ER) |
+ __ins_bit(9, X86_FEATURE_AVX512CD) |
+ __ins_bit(9, X86_FEATURE_SHA) |
+ __ins_bit(9, X86_FEATURE_AVX512BW) |
+ __ins_bit(9, X86_FEATURE_AVXVL),
+
+ [10] =
+ __ins_bit(10, X86_FEATURE_XSAVEOPT) |
+ __ins_bit(10, X86_FEATURE_XSAVEC) |
+ __ins_bit(10, X86_FEATURE_XGETBV1) |
+ __ins_bit(10, X86_FEATURE_XSAVES),
+
+ [11] =
+ __ins_bit(11, X86_FEATURE_PREFETCHWT1),
+};
+
+#undef __ins_bit
+
+static int cpu_validate_ins_features(CpuinfoX86Entry *img_x86_entry)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(rt_cpu_info.x86_capability); i++) {
+ u32 s = img_x86_entry->capability[i] & x86_ins_capability_mask[i];
+ u32 d = rt_cpu_info.x86_capability[i] & x86_ins_capability_mask[i];
+
+ /*
+ * Destination might be more feature rich
+ * but not the reverse.
+ */
+ if (s & ~d) {
+ pr_err("CPU instruction capabilities do not match run time\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int cpu_validate_features(CpuinfoX86Entry *img_x86_entry)
+{
+ if (img_x86_entry->n_capability != ARRAY_SIZE(rt_cpu_info.x86_capability)) {
+ /*
+ * Image carries different number of bits.
+ * Simply reject, we can't guarantee anything
+ * in such case.
+ */
+ pr_err("Size of features in image mismatch "
+ "one provided by run time CPU (%d:%d)\n",
+ (unsigned)img_x86_entry->n_capability,
+ (unsigned)ARRAY_SIZE(rt_cpu_info.x86_capability));
+ return -1;
+ }
+
+ if (opts.cpu_cap == CPU_CAP_FPU) {
+ /*
+ * If we're requested to check FPU only ignore
+ * any other bit. It's up to a user if the
+ * rest of mismatches won't cause problems.
+ */
+
+#define __mismatch_fpu_bit(__bit) \
+ (test_bit(__bit, (void *)img_x86_entry->capability) && \
+ !cpu_has_feature(__bit))
+ if (__mismatch_fpu_bit(X86_FEATURE_FPU) ||
+ __mismatch_fpu_bit(X86_FEATURE_FXSR) ||
+ __mismatch_fpu_bit(X86_FEATURE_XSAVE)) {
+ pr_err("FPU feature required by image "
+ "is not supported on host.\n");
+ return -1;
+ } else
+ return 0;
+#undef __mismatch_fpu_bit
+ }
+
+ /*
+ * Capability on instructions level only.
+ */
+ if (opts.cpu_cap == CPU_CAP_INS)
+ return cpu_validate_ins_features(img_x86_entry);
+
+ /*
+ * Strict capability mode. Everything must match.
+ */
+ if (memcmp(img_x86_entry->capability, rt_cpu_info.x86_capability,
+ sizeof(rt_cpu_info.x86_capability))) {
+ pr_err("CPU capabilites do not match run time\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int cpu_validate_cpuinfo(void)
+{
+ CpuinfoX86Entry *img_x86_entry;
+ CpuinfoEntry *img_cpu_info;
+ struct cr_img *img;
+ int ret = -1;
+
+ img = open_image(CR_FD_CPUINFO, O_RSTR | O_OPT);
+ if (!img)
+ return -1;
+
+ if (pb_read_one(img, &img_cpu_info, PB_CPUINFO) < 0)
+ goto err;
+
+ if (img_cpu_info->n_x86_entry != 1) {
+ pr_err("No x86 related cpuinfo in image, "
+ "corruption (n_x86_entry = %zi)\n",
+ img_cpu_info->n_x86_entry);
+ goto err;
+ }
+
+ img_x86_entry = img_cpu_info->x86_entry[0];
+ if (img_x86_entry->vendor_id != CPUINFO_X86_ENTRY__VENDOR__INTEL &&
+ img_x86_entry->vendor_id != CPUINFO_X86_ENTRY__VENDOR__AMD) {
+ pr_err("Unknown cpu vendor %d\n", img_x86_entry->vendor_id);
+ goto err;
+ }
+
+ if (img_x86_entry->n_capability != ARRAY_SIZE(rt_cpu_info.x86_capability)) {
+ pr_err("Image carries %u words while %u expected\n",
+ (unsigned)img_x86_entry->n_capability,
+ (unsigned)ARRAY_SIZE(rt_cpu_info.x86_capability));
+ goto err;
+ }
+
+ ret = cpu_validate_features(img_x86_entry);
+err:
+ close_image(img);
+ return ret;
+}
+
+int cpuinfo_dump(void)
+{
+ if (cpu_init())
+ return -1;
+ if (cpu_dump_cpuinfo())
+ return -1;
+ return 0;
+}
+
+int cpuinfo_check(void)
+{
+ if (cpu_init())
+ return 1;
+
+ /*
+ * Force to check all caps if empty passed,
+ * still allow to check instructions only
+ * and etc.
+ */
+ if (!opts.cpu_cap)
+ opts.cpu_cap = CPU_CAP_ALL;
+
+ if (cpu_validate_cpuinfo())
+ return 1;
+
+ return 0;
+}
diff --git a/arch/x86/crtools.c b/arch/x86/crtools.c
new file mode 100644
index 000000000..cbbcb9df0
--- /dev/null
+++ b/arch/x86/crtools.c
@@ -0,0 +1,555 @@
+#include
+#include
+#include
+#include
+
+#include "asm/processor-flags.h"
+#include "asm/restorer.h"
+#include "asm/types.h"
+#include "asm/fpu.h"
+
+#include "cr_options.h"
+#include "compiler.h"
+#include "ptrace.h"
+#include "parasite-syscall.h"
+#include "restorer.h"
+#include "syscall.h"
+#include "log.h"
+#include "util.h"
+#include "cpu.h"
+#include "errno.h"
+
+#include "protobuf.h"
+#include "protobuf/core.pb-c.h"
+#include "protobuf/creds.pb-c.h"
+
+/*
+ * Injected syscall instruction
+ */
+const char code_syscall[] = {
+ 0x0f, 0x05, /* syscall */
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc /* int 3, ... */
+};
+
+const int code_syscall_size = round_up(sizeof(code_syscall), sizeof(long));
+
+static inline void __check_code_syscall(void)
+{
+ BUILD_BUG_ON(sizeof(code_syscall) != BUILTIN_SYSCALL_SIZE);
+ BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
+}
+
+void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *regs)
+{
+ regs->ip = new_ip;
+ if (stack)
+ regs->sp = (unsigned long) stack;
+
+ /* Avoid end of syscall processing */
+ regs->orig_ax = -1;
+
+ /* Make sure flags are in known state */
+ regs->flags &= ~(X86_EFLAGS_TF | X86_EFLAGS_DF | X86_EFLAGS_IF);
+}
+
+static int task_in_compat_mode(pid_t pid)
+{
+ unsigned long cs, ds;
+
+ errno = 0;
+ cs = ptrace(PTRACE_PEEKUSER, pid, offsetof(user_regs_struct_t, cs), 0);
+ if (errno != 0) {
+ pr_perror("Can't get CS register for %d", pid);
+ return -1;
+ }
+
+ errno = 0;
+ ds = ptrace(PTRACE_PEEKUSER, pid, offsetof(user_regs_struct_t, ds), 0);
+ if (errno != 0) {
+ pr_perror("Can't get DS register for %d", pid);
+ return -1;
+ }
+
+ /* It's x86-32 or x32 */
+ return cs != 0x33 || ds == 0x2b;
+}
+
+bool arch_can_dump_task(pid_t pid)
+{
+ if (task_in_compat_mode(pid)) {
+ pr_err("Can't dump task %d running in 32-bit mode\n", pid);
+ return false;
+ }
+
+ return true;
+}
+
+int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret,
+ unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5,
+ unsigned long arg6)
+{
+ user_regs_struct_t regs = ctl->orig.regs;
+ int err;
+
+ regs.ax = (unsigned long)nr;
+ regs.di = arg1;
+ regs.si = arg2;
+ regs.dx = arg3;
+ regs.r10 = arg4;
+ regs.r8 = arg5;
+ regs.r9 = arg6;
+
+ err = __parasite_execute_syscall(ctl, ®s);
+
+ *ret = regs.ax;
+ return err;
+}
+
+int get_task_regs(pid_t pid, user_regs_struct_t regs, CoreEntry *core)
+{
+ struct xsave_struct xsave = { };
+
+ struct iovec iov;
+ int ret = -1;
+
+ pr_info("Dumping GP/FPU registers for %d\n", pid);
+
+ /* Did we come from a system call? */
+ if ((int)regs.orig_ax >= 0) {
+ /* Restart the system call */
+ switch ((long)(int)regs.ax) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ regs.ax = regs.orig_ax;
+ regs.ip -= 2;
+ break;
+ case -ERESTART_RESTARTBLOCK:
+ regs.ax = __NR_restart_syscall;
+ regs.ip -= 2;
+ break;
+ }
+ }
+
+#define assign_reg(dst, src, e) do { dst->e = (__typeof__(dst->e))src.e; } while (0)
+#define assign_array(dst, src, e) memcpy(dst->e, &src.e, sizeof(src.e))
+
+ assign_reg(core->thread_info->gpregs, regs, r15);
+ assign_reg(core->thread_info->gpregs, regs, r14);
+ assign_reg(core->thread_info->gpregs, regs, r13);
+ assign_reg(core->thread_info->gpregs, regs, r12);
+ assign_reg(core->thread_info->gpregs, regs, bp);
+ assign_reg(core->thread_info->gpregs, regs, bx);
+ assign_reg(core->thread_info->gpregs, regs, r11);
+ assign_reg(core->thread_info->gpregs, regs, r10);
+ assign_reg(core->thread_info->gpregs, regs, r9);
+ assign_reg(core->thread_info->gpregs, regs, r8);
+ assign_reg(core->thread_info->gpregs, regs, ax);
+ assign_reg(core->thread_info->gpregs, regs, cx);
+ assign_reg(core->thread_info->gpregs, regs, dx);
+ assign_reg(core->thread_info->gpregs, regs, si);
+ assign_reg(core->thread_info->gpregs, regs, di);
+ assign_reg(core->thread_info->gpregs, regs, orig_ax);
+ assign_reg(core->thread_info->gpregs, regs, ip);
+ assign_reg(core->thread_info->gpregs, regs, cs);
+ assign_reg(core->thread_info->gpregs, regs, flags);
+ assign_reg(core->thread_info->gpregs, regs, sp);
+ assign_reg(core->thread_info->gpregs, regs, ss);
+ assign_reg(core->thread_info->gpregs, regs, fs_base);
+ assign_reg(core->thread_info->gpregs, regs, gs_base);
+ assign_reg(core->thread_info->gpregs, regs, ds);
+ assign_reg(core->thread_info->gpregs, regs, es);
+ assign_reg(core->thread_info->gpregs, regs, fs);
+ assign_reg(core->thread_info->gpregs, regs, gs);
+
+#ifndef PTRACE_GETREGSET
+# define PTRACE_GETREGSET 0x4204
+#endif
+
+ if (!cpu_has_feature(X86_FEATURE_FPU))
+ goto out;
+
+ /*
+ * FPU fetched either via fxsave or via xsave,
+ * thus decode it accrodingly.
+ */
+
+ if (cpu_has_feature(X86_FEATURE_XSAVE)) {
+ iov.iov_base = &xsave;
+ iov.iov_len = sizeof(xsave);
+
+ if (ptrace(PTRACE_GETREGSET, pid, (unsigned int)NT_X86_XSTATE, &iov) < 0) {
+ pr_perror("Can't obtain FPU registers for %d", pid);
+ goto err;
+ }
+ } else {
+ if (ptrace(PTRACE_GETFPREGS, pid, NULL, &xsave)) {
+ pr_perror("Can't obtain FPU registers for %d", pid);
+ goto err;
+ }
+ }
+
+ assign_reg(core->thread_info->fpregs, xsave.i387, cwd);
+ assign_reg(core->thread_info->fpregs, xsave.i387, swd);
+ assign_reg(core->thread_info->fpregs, xsave.i387, twd);
+ assign_reg(core->thread_info->fpregs, xsave.i387, fop);
+ assign_reg(core->thread_info->fpregs, xsave.i387, rip);
+ assign_reg(core->thread_info->fpregs, xsave.i387, rdp);
+ assign_reg(core->thread_info->fpregs, xsave.i387, mxcsr);
+ assign_reg(core->thread_info->fpregs, xsave.i387, mxcsr_mask);
+
+ /* Make sure we have enough space */
+ BUG_ON(core->thread_info->fpregs->n_st_space != ARRAY_SIZE(xsave.i387.st_space));
+ BUG_ON(core->thread_info->fpregs->n_xmm_space != ARRAY_SIZE(xsave.i387.xmm_space));
+
+ assign_array(core->thread_info->fpregs, xsave.i387, st_space);
+ assign_array(core->thread_info->fpregs, xsave.i387, xmm_space);
+
+ if (cpu_has_feature(X86_FEATURE_XSAVE)) {
+ BUG_ON(core->thread_info->fpregs->xsave->n_ymmh_space != ARRAY_SIZE(xsave.ymmh.ymmh_space));
+
+ assign_reg(core->thread_info->fpregs->xsave, xsave.xsave_hdr, xstate_bv);
+ assign_array(core->thread_info->fpregs->xsave, xsave.ymmh, ymmh_space);
+ }
+
+#undef assign_reg
+#undef assign_array
+
+out:
+ ret = 0;
+
+err:
+ return ret;
+}
+
+int arch_alloc_thread_info(CoreEntry *core)
+{
+ size_t sz;
+ bool with_fpu, with_xsave = false;
+ void *m;
+ ThreadInfoX86 *ti = NULL;
+
+
+ with_fpu = cpu_has_feature(X86_FEATURE_FPU);
+
+ sz = sizeof(ThreadInfoX86) + sizeof(UserX86RegsEntry);
+ if (with_fpu) {
+ sz += sizeof(UserX86FpregsEntry);
+ with_xsave = cpu_has_feature(X86_FEATURE_XSAVE);
+ if (with_xsave)
+ sz += sizeof(UserX86XsaveEntry);
+ }
+
+ m = xmalloc(sz);
+ if (!m)
+ return -1;
+
+ ti = core->thread_info = xptr_pull(&m, ThreadInfoX86);
+ thread_info_x86__init(ti);
+ ti->gpregs = xptr_pull(&m, UserX86RegsEntry);
+ user_x86_regs_entry__init(ti->gpregs);
+
+ if (with_fpu) {
+ UserX86FpregsEntry *fpregs;
+
+ fpregs = ti->fpregs = xptr_pull(&m, UserX86FpregsEntry);
+ user_x86_fpregs_entry__init(fpregs);
+
+ /* These are numbers from kernel */
+ fpregs->n_st_space = 32;
+ fpregs->n_xmm_space = 64;
+
+ fpregs->st_space = xzalloc(pb_repeated_size(fpregs, st_space));
+ fpregs->xmm_space = xzalloc(pb_repeated_size(fpregs, xmm_space));
+
+ if (!fpregs->st_space || !fpregs->xmm_space)
+ goto err;
+
+ if (with_xsave) {
+ UserX86XsaveEntry *xsave;
+
+ xsave = fpregs->xsave = xptr_pull(&m, UserX86XsaveEntry);
+ user_x86_xsave_entry__init(xsave);
+
+ xsave->n_ymmh_space = 64;
+ xsave->ymmh_space = xzalloc(pb_repeated_size(xsave, ymmh_space));
+ if (!xsave->ymmh_space)
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ return -1;
+}
+
+void arch_free_thread_info(CoreEntry *core)
+{
+ if (!core->thread_info)
+ return;
+
+ if (core->thread_info->fpregs->xsave)
+ xfree(core->thread_info->fpregs->xsave->ymmh_space);
+ xfree(core->thread_info->fpregs->st_space);
+ xfree(core->thread_info->fpregs->xmm_space);
+ xfree(core->thread_info);
+}
+
+static bool valid_xsave_frame(CoreEntry *core)
+{
+ struct xsave_struct *x = NULL;
+
+ if (core->thread_info->fpregs->n_st_space < ARRAY_SIZE(x->i387.st_space)) {
+ pr_err("Corruption in FPU st_space area "
+ "(got %li but %li expected)\n",
+ (long)core->thread_info->fpregs->n_st_space,
+ (long)ARRAY_SIZE(x->i387.st_space));
+ return false;
+ }
+
+ if (core->thread_info->fpregs->n_xmm_space < ARRAY_SIZE(x->i387.xmm_space)) {
+ pr_err("Corruption in FPU xmm_space area "
+ "(got %li but %li expected)\n",
+ (long)core->thread_info->fpregs->n_st_space,
+ (long)ARRAY_SIZE(x->i387.xmm_space));
+ return false;
+ }
+
+ if (cpu_has_feature(X86_FEATURE_XSAVE)) {
+ if (core->thread_info->fpregs->xsave &&
+ core->thread_info->fpregs->xsave->n_ymmh_space < ARRAY_SIZE(x->ymmh.ymmh_space)) {
+ pr_err("Corruption in FPU ymmh_space area "
+ "(got %li but %li expected)\n",
+ (long)core->thread_info->fpregs->xsave->n_ymmh_space,
+ (long)ARRAY_SIZE(x->ymmh.ymmh_space));
+ return false;
+ }
+ } else {
+ /*
+ * If the image has xsave area present then CPU we're restoring
+ * on must have X86_FEATURE_XSAVE feature until explicitly
+ * stated in options.
+ */
+ if (core->thread_info->fpregs->xsave) {
+ if (opts.cpu_cap & CPU_CAP_FPU) {
+ pr_err("FPU xsave area present, "
+ "but host cpu doesn't support it\n");
+ return false;
+ } else
+ pr_warn_once("FPU is about to restore ignoring ymm state!\n");
+ }
+ }
+
+ return true;
+}
+
+static void show_rt_xsave_frame(struct xsave_struct *x)
+{
+ struct fpx_sw_bytes *fpx = (void *)&x->i387.sw_reserved;
+ struct xsave_hdr_struct *xsave_hdr = &x->xsave_hdr;
+ struct i387_fxsave_struct *i387 = &x->i387;
+
+ pr_debug("xsave runtime structure\n");
+ pr_debug("-----------------------\n");
+
+ pr_debug("cwd:%x swd:%x twd:%x fop:%x mxcsr:%x mxcsr_mask:%x\n",
+ (int)i387->cwd, (int)i387->swd, (int)i387->twd,
+ (int)i387->fop, (int)i387->mxcsr, (int)i387->mxcsr_mask);
+
+ pr_debug("magic1:%x extended_size:%x xstate_bv:%lx xstate_size:%x\n",
+ fpx->magic1, fpx->extended_size, fpx->xstate_bv, fpx->xstate_size);
+
+ pr_debug("xstate_bv: %lx\n", xsave_hdr->xstate_bv);
+
+ pr_debug("-----------------------\n");
+}
+
+int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
+{
+ fpu_state_t *fpu_state = &sigframe->fpu_state;
+ struct xsave_struct *x = &fpu_state->xsave;
+
+ /*
+ * If no FPU information provided -- we're restoring
+ * old image which has no FPU support, or the dump simply
+ * has no FPU support at all.
+ */
+ if (!core->thread_info->fpregs) {
+ fpu_state->has_fpu = false;
+ return 0;
+ }
+
+ if (!valid_xsave_frame(core))
+ return -1;
+
+ fpu_state->has_fpu = true;
+
+#define assign_reg(dst, src, e) do { dst.e = (__typeof__(dst.e))src->e; } while (0)
+#define assign_array(dst, src, e) memcpy(dst.e, (src)->e, sizeof(dst.e))
+
+ assign_reg(x->i387, core->thread_info->fpregs, cwd);
+ assign_reg(x->i387, core->thread_info->fpregs, swd);
+ assign_reg(x->i387, core->thread_info->fpregs, twd);
+ assign_reg(x->i387, core->thread_info->fpregs, fop);
+ assign_reg(x->i387, core->thread_info->fpregs, rip);
+ assign_reg(x->i387, core->thread_info->fpregs, rdp);
+ assign_reg(x->i387, core->thread_info->fpregs, mxcsr);
+ assign_reg(x->i387, core->thread_info->fpregs, mxcsr_mask);
+
+ assign_array(x->i387, core->thread_info->fpregs, st_space);
+ assign_array(x->i387, core->thread_info->fpregs, xmm_space);
+
+ if (cpu_has_feature(X86_FEATURE_XSAVE)) {
+ struct fpx_sw_bytes *fpx_sw = (void *)&x->i387.sw_reserved;
+ void *magic2;
+
+ x->xsave_hdr.xstate_bv = XSTATE_FP | XSTATE_SSE | XSTATE_YMM;
+
+ /*
+ * fpregs->xsave pointer might not present on image so we
+ * simply clear out all ymm registers.
+ */
+ if (core->thread_info->fpregs->xsave)
+ assign_array(x->ymmh, core->thread_info->fpregs->xsave, ymmh_space);
+
+ fpx_sw->magic1 = FP_XSTATE_MAGIC1;
+ fpx_sw->xstate_bv = XSTATE_FP | XSTATE_SSE | XSTATE_YMM;
+ fpx_sw->xstate_size = sizeof(struct xsave_struct);
+ fpx_sw->extended_size = sizeof(struct xsave_struct) + FP_XSTATE_MAGIC2_SIZE;
+
+ /*
+ * This should be at the end of xsave frame.
+ */
+ magic2 = fpu_state->__pad + sizeof(struct xsave_struct);
+ *(u32 *)magic2 = FP_XSTATE_MAGIC2;
+ }
+
+ show_rt_xsave_frame(x);
+
+#undef assign_reg
+#undef assign_array
+
+ return 0;
+}
+
+void *mmap_seized(struct parasite_ctl *ctl,
+ void *addr, size_t length, int prot,
+ int flags, int fd, off_t offset)
+{
+ unsigned long map;
+ int err;
+
+ err = syscall_seized(ctl, __NR_mmap, &map,
+ (unsigned long)addr, length, prot, flags, fd, offset);
+ if (err < 0 || map > TASK_SIZE)
+ map = 0;
+
+ return (void *)map;
+}
+
+int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r)
+{
+#define CPREG1(d) f->uc.uc_mcontext.d = r->d
+#define CPREG2(d, s) f->uc.uc_mcontext.d = r->s
+
+ CPREG1(r8);
+ CPREG1(r9);
+ CPREG1(r10);
+ CPREG1(r11);
+ CPREG1(r12);
+ CPREG1(r13);
+ CPREG1(r14);
+ CPREG1(r15);
+ CPREG2(rdi, di);
+ CPREG2(rsi, si);
+ CPREG2(rbp, bp);
+ CPREG2(rbx, bx);
+ CPREG2(rdx, dx);
+ CPREG2(rax, ax);
+ CPREG2(rcx, cx);
+ CPREG2(rsp, sp);
+ CPREG2(rip, ip);
+ CPREG2(eflags, flags);
+ CPREG1(cs);
+ CPREG1(gs);
+ CPREG1(fs);
+
+ return 0;
+}
+
+int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe, fpu_state_t *fpu_state)
+{
+ unsigned long addr = (unsigned long)(void *)&fpu_state->xsave;
+
+ if ((addr % 64ul) == 0ul) {
+ sigframe->uc.uc_mcontext.fpstate = &fpu_state->xsave;
+ } else {
+ pr_err("Unaligned address passed: %lx\n", addr);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Copied from the gdb header gdb/nat/x86-dregs.h */
+
+/* Debug registers' indices. */
+#define DR_FIRSTADDR 0
+#define DR_LASTADDR 3
+#define DR_NADDR 4 /* The number of debug address registers. */
+#define DR_STATUS 6 /* Index of debug status register (DR6). */
+#define DR_CONTROL 7 /* Index of debug control register (DR7). */
+
+#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */
+#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */
+#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */
+
+/* Locally enable the break/watchpoint in the I'th debug register. */
+#define X86_DR_LOCAL_ENABLE(i) (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
+
+int ptrace_set_breakpoint(pid_t pid, void *addr)
+{
+ int ret;
+
+ /* Set a breakpoint */
+ if (ptrace(PTRACE_POKEUSER, pid,
+ offsetof(struct user, u_debugreg[DR_FIRSTADDR]),
+ addr)) {
+ pr_err("Unable to setup a breakpoint\n");
+ return -1;
+ }
+
+ /* Enable the breakpoint */
+ if (ptrace(PTRACE_POKEUSER, pid,
+ offsetof(struct user, u_debugreg[DR_CONTROL]),
+ X86_DR_LOCAL_ENABLE(DR_FIRSTADDR))) {
+ pr_err("Unable to enable the breakpoint\n");
+ return -1;
+ }
+
+ ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
+ if (ret) {
+ pr_perror("Unable to restart the stopped tracee process");
+ return -1;
+ }
+
+ return 1;
+}
+
+int ptrace_flush_breakpoints(pid_t pid)
+{
+ /* Disable the breakpoint */
+ if (ptrace(PTRACE_POKEUSER, pid,
+ offsetof(struct user, u_debugreg[DR_CONTROL]),
+ 0)) {
+ pr_err("Unable to disable the breakpoint\n");
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/arch/x86/include/asm/atomic.h b/arch/x86/include/asm/atomic.h
new file mode 100644
index 000000000..d447b65cb
--- /dev/null
+++ b/arch/x86/include/asm/atomic.h
@@ -0,0 +1,78 @@
+#ifndef __CR_ATOMIC_H__
+#define __CR_ATOMIC_H__
+
+#include "asm/cmpxchg.h"
+
+#define LOCK_PREFIX "\n\tlock; "
+
+typedef struct {
+ int counter;
+} atomic_t;
+
+#define ATOMIC_INIT(i) { (i) }
+
+static inline int atomic_read(const atomic_t *v)
+{
+ return (*(volatile int *)&(v)->counter);
+}
+
+static inline void atomic_set(atomic_t *v, int i)
+{
+ v->counter = i;
+}
+
+static inline void atomic_add(int i, atomic_t *v)
+{
+ asm volatile(LOCK_PREFIX "addl %1,%0"
+ : "+m" (v->counter)
+ : "ir" (i));
+}
+
+static inline void atomic_sub(int i, atomic_t *v)
+{
+ asm volatile(LOCK_PREFIX "subl %1,%0"
+ : "+m" (v->counter)
+ : "ir" (i));
+}
+
+static inline void atomic_inc(atomic_t *v)
+{
+ asm volatile(LOCK_PREFIX "incl %0"
+ : "+m" (v->counter));
+}
+
+static inline void atomic_dec(atomic_t *v)
+{
+ asm volatile(LOCK_PREFIX "decl %0"
+ : "+m" (v->counter));
+}
+
+static inline int atomic_dec_and_test(atomic_t *v)
+{
+ unsigned char c;
+
+ asm volatile(LOCK_PREFIX "decl %0; sete %1"
+ : "+m" (v->counter), "=qm" (c)
+ : : "memory");
+ return c != 0;
+}
+
+static inline int atomic_add_return(int i, atomic_t *v)
+{
+ return i + xadd(&v->counter, i);
+}
+
+static inline int atomic_sub_return(int i, atomic_t *v)
+{
+ return atomic_add_return(-i, v);
+}
+
+#define atomic_inc_return(v) (atomic_add_return(1, v))
+#define atomic_dec_return(v) (atomic_sub_return(1, v))
+
+static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
+{
+ return cmpxchg(&v->counter, old, new);
+}
+
+#endif /* __CR_ATOMIC_H__ */
diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h
new file mode 100644
index 000000000..7d6283183
--- /dev/null
+++ b/arch/x86/include/asm/bitops.h
@@ -0,0 +1,113 @@
+#ifndef __CR_BITOPS_H__
+#define __CR_BITOPS_H__
+
+#include "asm/bitsperlong.h"
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_LONG)
+
+#define DECLARE_BITMAP(name, bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)
+/* Technically wrong, but this avoids compilation errors on some gcc
+ versions. */
+#define BITOP_ADDR(x) "=m" (*(volatile long *) (x))
+#else
+#define BITOP_ADDR(x) "+m" (*(volatile long *) (x))
+#endif
+
+#define ADDR BITOP_ADDR(addr)
+
+static inline void set_bit(int nr, volatile unsigned long *addr)
+{
+ asm volatile("btsl %1,%0" : ADDR : "Ir" (nr) : "memory");
+}
+
+static inline void change_bit(int nr, volatile unsigned long *addr)
+{
+ asm volatile("btcl %1,%0" : ADDR : "Ir" (nr));
+}
+
+static inline int test_bit(int nr, volatile const unsigned long *addr)
+{
+ int oldbit;
+
+ asm volatile("bt %2,%1\n\t"
+ "sbb %0,%0"
+ : "=r" (oldbit)
+ : "m" (*(unsigned long *)addr), "Ir" (nr));
+
+ return oldbit;
+}
+
+static inline void clear_bit(int nr, volatile unsigned long *addr)
+{
+ asm volatile("btrl %1,%0" : ADDR : "Ir" (nr));
+}
+
+/**
+ * __ffs - find first set bit in word
+ * @word: The word to search
+ *
+ * Undefined if no bit exists, so code should check against 0 first.
+ */
+static inline unsigned long __ffs(unsigned long word)
+{
+ asm("bsf %1,%0"
+ : "=r" (word)
+ : "rm" (word));
+ return word;
+}
+
+#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
+
+/*
+ * Find the next set bit in a memory region.
+ */
+static inline
+unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
+ unsigned long offset)
+{
+ const unsigned long *p = addr + BITOP_WORD(offset);
+ unsigned long result = offset & ~(BITS_PER_LONG-1);
+ unsigned long tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset %= BITS_PER_LONG;
+ if (offset) {
+ tmp = *(p++);
+ tmp &= (~0UL << offset);
+ if (size < BITS_PER_LONG)
+ goto found_first;
+ if (tmp)
+ goto found_middle;
+ size -= BITS_PER_LONG;
+ result += BITS_PER_LONG;
+ }
+ while (size & ~(BITS_PER_LONG-1)) {
+ if ((tmp = *(p++)))
+ goto found_middle;
+ result += BITS_PER_LONG;
+ size -= BITS_PER_LONG;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
+
+found_first:
+ tmp &= (~0UL >> (BITS_PER_LONG - size));
+ if (tmp == 0UL) /* Are any bits set? */
+ return result + size; /* Nope. */
+found_middle:
+ return result + __ffs(tmp);
+}
+
+#define for_each_bit(i, bitmask) \
+ for (i = find_next_bit(bitmask, sizeof(bitmask), 0); \
+ i < sizeof(bitmask); \
+ i = find_next_bit(bitmask, sizeof(bitmask), i + 1))
+
+#endif /* __CR_BITOPS_H__ */
diff --git a/arch/x86/include/asm/bitsperlong.h b/arch/x86/include/asm/bitsperlong.h
new file mode 100644
index 000000000..7e0a71e8d
--- /dev/null
+++ b/arch/x86/include/asm/bitsperlong.h
@@ -0,0 +1,10 @@
+#ifndef __CR_BITSPERLONG_H__
+#define __CR_BITSPERLONG_H__
+
+#ifdef CONFIG_X86_64
+# define BITS_PER_LONG 64
+#else
+# define BITS_PER_LONG 32
+#endif
+
+#endif /* __CR_BITSPERLONG_H__ */
diff --git a/arch/x86/include/asm/cmpxchg.h b/arch/x86/include/asm/cmpxchg.h
new file mode 100644
index 000000000..600d0a7ff
--- /dev/null
+++ b/arch/x86/include/asm/cmpxchg.h
@@ -0,0 +1,105 @@
+#ifndef __CR_CMPXCHG_H__
+#define __CR_CMPXCHG_H__
+
+#include "asm/int.h"
+
+#define __X86_CASE_B 1
+#define __X86_CASE_W 2
+#define __X86_CASE_L 4
+#define __X86_CASE_Q 8
+
+/*
+ * An exchange-type operation, which takes a value and a pointer, and
+ * returns the old value. Make sure you never reach non-case statement
+ * here, otherwise behaviour is undefined.
+ */
+#define __xchg_op(ptr, arg, op, lock) \
+ ({ \
+ __typeof__ (*(ptr)) __ret = (arg); \
+ switch (sizeof(*(ptr))) { \
+ case __X86_CASE_B: \
+ asm volatile (lock #op "b %b0, %1\n" \
+ : "+q" (__ret), "+m" (*(ptr)) \
+ : : "memory", "cc"); \
+ break; \
+ case __X86_CASE_W: \
+ asm volatile (lock #op "w %w0, %1\n" \
+ : "+r" (__ret), "+m" (*(ptr)) \
+ : : "memory", "cc"); \
+ break; \
+ case __X86_CASE_L: \
+ asm volatile (lock #op "l %0, %1\n" \
+ : "+r" (__ret), "+m" (*(ptr)) \
+ : : "memory", "cc"); \
+ break; \
+ case __X86_CASE_Q: \
+ asm volatile (lock #op "q %q0, %1\n" \
+ : "+r" (__ret), "+m" (*(ptr)) \
+ : : "memory", "cc"); \
+ break; \
+ } \
+ __ret; \
+ })
+
+#define __xadd(ptr, inc, lock) __xchg_op((ptr), (inc), xadd, lock)
+#define xadd(ptr, inc) __xadd((ptr), (inc), "lock ;")
+
+/* Borrowed from linux kernel arch/x86/include/asm/cmpxchg.h */
+
+/*
+ * Atomic compare and exchange. Compare OLD with MEM, if identical,
+ * store NEW in MEM. Return the initial value in MEM. Success is
+ * indicated by comparing RETURN with OLD.
+ */
+#define __raw_cmpxchg(ptr, old, new, size, lock) \
+({ \
+ __typeof__(*(ptr)) __ret; \
+ __typeof__(*(ptr)) __old = (old); \
+ __typeof__(*(ptr)) __new = (new); \
+ switch (size) { \
+ case __X86_CASE_B: \
+ { \
+ volatile u8 *__ptr = (volatile u8 *)(ptr); \
+ asm volatile(lock "cmpxchgb %2,%1" \
+ : "=a" (__ret), "+m" (*__ptr) \
+ : "q" (__new), "0" (__old) \
+ : "memory"); \
+ break; \
+ } \
+ case __X86_CASE_W: \
+ { \
+ volatile u16 *__ptr = (volatile u16 *)(ptr); \
+ asm volatile(lock "cmpxchgw %2,%1" \
+ : "=a" (__ret), "+m" (*__ptr) \
+ : "r" (__new), "0" (__old) \
+ : "memory"); \
+ break; \
+ } \
+ case __X86_CASE_L: \
+ { \
+ volatile u32 *__ptr = (volatile u32 *)(ptr); \
+ asm volatile(lock "cmpxchgl %2,%1" \
+ : "=a" (__ret), "+m" (*__ptr) \
+ : "r" (__new), "0" (__old) \
+ : "memory"); \
+ break; \
+ } \
+ case __X86_CASE_Q: \
+ { \
+ volatile u64 *__ptr = (volatile u64 *)(ptr); \
+ asm volatile(lock "cmpxchgq %2,%1" \
+ : "=a" (__ret), "+m" (*__ptr) \
+ : "r" (__new), "0" (__old) \
+ : "memory"); \
+ break; \
+ } \
+ } \
+ __ret; \
+})
+
+#define __cmpxchg(ptr, old, new, size) \
+ __raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX)
+#define cmpxchg(ptr, old, new) \
+ __cmpxchg(ptr, old, new, sizeof(*(ptr)))
+
+#endif /* __CR_CMPXCHG_H__ */
diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h
new file mode 100644
index 000000000..6f49229d6
--- /dev/null
+++ b/arch/x86/include/asm/cpu.h
@@ -0,0 +1,207 @@
+#ifndef __CR_ASM_CPU_H__
+#define __CR_ASM_CPU_H__
+
+#include "asm/types.h"
+
+/*
+ * Adopted from linux kernel and enhanced from Intel/AMD manuals.
+ */
+
+#define NCAPINTS (12) /* N 32-bit words worth of info */
+#define NCAPINTS_BITS (NCAPINTS * 32)
+
+#define X86_FEATURE_FPU (0*32+ 0) /* Onboard FPU */
+#define X86_FEATURE_VME (0*32+ 1) /* Virtual 8086 Mode Enhancements */
+#define X86_FEATURE_DE (0*32+ 2) /* Debugging Extensions */
+#define X86_FEATURE_PSE (0*32+ 3) /* Page Size Extension */
+#define X86_FEATURE_TSC (0*32+ 4) /* Time Stamp Counter */
+#define X86_FEATURE_MSR (0*32+ 5) /* Model Specific Registers RDMSR and WRMSR Instructions */
+#define X86_FEATURE_PAE (0*32+ 6) /* Physical Address Extension */
+#define X86_FEATURE_MCE (0*32+ 7) /* Machine Check Exception */
+#define X86_FEATURE_CX8 (0*32+ 8) /* CMPXCHG8 instruction */
+#define X86_FEATURE_APIC (0*32+ 9) /* APIC On-Chip */
+#define X86_FEATURE_SEP (0*32+11) /* SYSENTER and SYSEXIT Instructions */
+#define X86_FEATURE_MTRR (0*32+12) /* Memory Type Range Registers */
+#define X86_FEATURE_PGE (0*32+13) /* PTE Global Bit */
+#define X86_FEATURE_MCA (0*32+14) /* Machine Check Architecture */
+#define X86_FEATURE_CMOV (0*32+15) /* CMOV instructions (plus FCMOVcc, FCOMI with FPU) */
+#define X86_FEATURE_PAT (0*32+16) /* Page Attribute Table */
+#define X86_FEATURE_PSE36 (0*32+17) /* 36-Bit Page Size Extension */
+#define X86_FEATURE_PSN (0*32+18) /* Processor Serial Number */
+#define X86_FEATURE_DS (0*32+21) /* Debug Store */
+#define X86_FEATURE_CLFLUSH (0*32+19) /* CLFLUSH instruction */
+#define X86_FEATURE_ACPI (0*32+22) /* Thermal Monitor and Software Controlled Clock Facilities */
+#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
+#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE/FXRSTOR, CR4.OSFXSR */
+#define X86_FEATURE_XMM (0*32+25) /* "sse" */
+#define X86_FEATURE_XMM2 (0*32+26) /* "sse2" */
+#define X86_FEATURE_SS (0*32+27) /* Self Snoop */
+#define X86_FEATURE_HTT (0*32+28) /* Multi-Threading */
+#define X86_FEATURE_TM (0*32+29) /* Thermal Monitor */
+#define X86_FEATURE_PBE (0*32+31) /* Pending Break Enable */
+
+#define X86_FEATURE_SYSCALL (1*32+11) /* SYSCALL/SYSRET */
+#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
+#define X86_FEATURE_RDTSCP (1*32+27) /* RDTSCP */
+#define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */
+#define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */
+
+#define X86_FEATURE_REP_GOOD (3*32+16) /* rep microcode works well */
+#define X86_FEATURE_NOPL (3*32+20) /* The NOPL (0F 1F) instructions */
+
+#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */
+#define X86_FEATURE_PCLMULQDQ (4*32+ 1) /* PCLMULQDQ instruction */
+#define X86_FEATURE_DTES64 (4*32+ 2) /* 64-bit DS Area */
+#define X86_FEATURE_MWAIT (4*32+ 3) /* "monitor" Monitor/Mwait support */
+#define X86_FEATURE_DSCPL (4*32+ 4) /* CPL Qualified Debug Store */
+#define X86_FEATURE_VMX (4*32+ 5) /* Virtual Machine Extensions */
+#define X86_FEATURE_SMX (4*32+ 6) /* Safer Mode Extensions */
+#define X86_FEATURE_EST (4*32+ 7) /* Enhanced Intel SpeedStep technology */
+#define X86_FEATURE_TM2 (4*32+ 8) /* Thermal Monitor 2 */
+#define X86_FEATURE_SSSE3 (4*32+ 9) /* Supplemental SSE-3 */
+#define X86_FEATURE_CNXTID (4*32+10) /* L1 Context ID */
+#define X86_FEATURE_FMA (4*32+12) /* Fused multiply-add */
+#define X86_FEATURE_CX16 (4*32+13) /* CMPXCHG16B */
+#define X86_FEATURE_XTPR_UCTL (4*32+14) /* xTPR Update Control */
+#define X86_FEATURE_PDCM (4*32+15) /* Perfmon and Debug Capability */
+#define X86_FEATURE_PCID (4*32+17) /* Process-context identifiers */
+#define X86_FEATURE_DCA (4*32+18) /* Ability to prefetch data from a memory mapped device */
+#define X86_FEATURE_XMM4_1 (4*32+19) /* "sse4_1" SSE-4.1 */
+#define X86_FEATURE_XMM4_2 (4*32+20) /* "sse4_2" SSE-4.2 */
+#define X86_FEATURE_X2APIC (4*32+21) /* x2APIC */
+#define X86_FEATURE_MOVBE (4*32+22) /* MOVBE instruction */
+#define X86_FEATURE_POPCNT (4*32+23) /* POPCNT instruction */
+#define X86_FEATURE_TSCDL (4*32+24) /* Local APIC timer supports one-shot operation using a TSC deadline value */
+#define X86_FEATURE_AES (4*32+25) /* AES instructions */
+#define X86_FEATURE_XSAVE (4*32+26) /* XSAVE/XRSTOR/XSETBV/XGETBV */
+#define X86_FEATURE_OSXSAVE (4*32+27) /* "" XSAVE enabled in the OS */
+#define X86_FEATURE_AVX (4*32+28) /* Advanced Vector Extensions */
+#define X86_FEATURE_F16C (4*32+29) /* 16-bit fp conversions */
+#define X86_FEATURE_RDRAND (4*32+30) /* The RDRAND instruction */
+
+#define X86_FEATURE_ABM (6*32+ 5) /* Advanced bit manipulation */
+#define X86_FEATURE_SSE4A (6*32+ 6) /* SSE-4A */
+#define X86_FEATURE_MISALIGNSSE (6*32+ 7) /* Misaligned SSE mode */
+#define X86_FEATURE_3DNOWPREFETCH (6*32+ 8) /* 3DNow prefetch instructions */
+#define X86_FEATURE_XOP (6*32+11) /* extended AVX instructions */
+#define X86_FEATURE_FMA4 (6*32+16) /* 4 operands MAC instructions */
+#define X86_FEATURE_TBM (6*32+21) /* trailing bit manipulations */
+
+#define X86_FEATURE_FSGSBASE (9*32+ 0) /* Supports RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE */
+#define X86_FEATURE_BMI1 (9*32+ 3) /* 1st group bit manipulation extensions */
+#define X86_FEATURE_HLE (9*32+ 4) /* Hardware Lock Elision */
+#define X86_FEATURE_AVX2 (9*32+ 5) /* AVX2 instructions */
+#define X86_FEATURE_SMEP (9*32+ 7) /* Supervisor Mode Execution Protection */
+#define X86_FEATURE_BMI2 (9*32+ 8) /* 2nd group bit manipulation extensions */
+#define X86_FEATURE_ERMS (9*32+ 9) /* Enhanced REP MOVSB/STOSB */
+#define X86_FEATURE_INVPCID (9*32+10) /* Invalidate Processor Context ID */
+#define X86_FEATURE_RTM (9*32+11) /* Restricted Transactional Memory */
+#define X86_FEATURE_MPX (9*32+14) /* Memory Protection Extension */
+#define X86_FEATURE_AVX512F (9*32+16) /* AVX-512 Foundation */
+#define X86_FEATURE_AVX512DQ (9*32+17) /* AVX-512 Foundation */
+#define X86_FEATURE_RDSEED (9*32+18) /* The RDSEED instruction */
+#define X86_FEATURE_ADX (9*32+19) /* The ADCX and ADOX instructions */
+#define X86_FEATURE_SMAP (9*32+20) /* Supervisor Mode Access Prevention */
+#define X86_FEATURE_CLFLUSHOPT (9*32+23) /* CLFLUSHOPT instruction */
+#define X86_FEATURE_IPT (9*32+25) /* Intel Processor Trace */
+#define X86_FEATURE_AVX512PF (9*32+26) /* AVX-512 Prefetch */
+#define X86_FEATURE_AVX512ER (9*32+27) /* AVX-512 Exponential and Reciprocal */
+#define X86_FEATURE_AVX512CD (9*32+28) /* AVX-512 Conflict Detection */
+#define X86_FEATURE_SHA (9*32+29) /* Intel SHA extensions */
+#define X86_FEATURE_AVX512BW (9*32+30) /* AVX-512 */
+#define X86_FEATURE_AVXVL (9*32+31) /* AVX-512 */
+
+#define X86_FEATURE_XSAVEOPT (10*32+0) /* XSAVEOPT */
+#define X86_FEATURE_XSAVEC (10*32+1) /* XSAVEC */
+#define X86_FEATURE_XGETBV1 (10*32+2) /* XGETBV with ECX = 1 */
+#define X86_FEATURE_XSAVES (10*32+3) /* XSAVES/XRSTORS */
+
+/*
+ * Node 11 is our own, kernel has not such entry.
+ */
+#define X86_FEATURE_PREFETCHWT1 (11*32+0) /* The PREFETCHWT1 instruction */
+
+static inline void native_cpuid(unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ /* ecx is often an input as well as an output. */
+ asm volatile("cpuid"
+ : "=a" (*eax),
+ "=b" (*ebx),
+ "=c" (*ecx),
+ "=d" (*edx)
+ : "0" (*eax), "2" (*ecx)
+ : "memory");
+}
+
+static inline void cpuid(unsigned int op,
+ unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ *eax = op;
+ *ecx = 0;
+ native_cpuid(eax, ebx, ecx, edx);
+}
+
+static inline void cpuid_count(unsigned int op, int count,
+ unsigned int *eax, unsigned int *ebx,
+ unsigned int *ecx, unsigned int *edx)
+{
+ *eax = op;
+ *ecx = count;
+ native_cpuid(eax, ebx, ecx, edx);
+}
+
+static inline unsigned int cpuid_eax(unsigned int op)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ cpuid(op, &eax, &ebx, &ecx, &edx);
+ return eax;
+}
+
+static inline unsigned int cpuid_ecx(unsigned int op)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ cpuid(op, &eax, &ebx, &ecx, &edx);
+ return ecx;
+}
+
+static inline unsigned int cpuid_edx(unsigned int op)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ cpuid(op, &eax, &ebx, &ecx, &edx);
+ return edx;
+}
+
+#define X86_FEATURE_VERSION 1
+
+enum {
+ X86_VENDOR_INTEL = 0,
+ X86_VENDOR_AMD = 1,
+
+ X86_VENDOR_MAX
+};
+
+struct cpuinfo_x86 {
+ u8 x86_family;
+ u8 x86_vendor;
+ u8 x86_model;
+ u8 x86_mask;
+ u32 x86_capability[NCAPINTS];
+ u32 extended_cpuid_level;
+ int cpuid_level;
+ char x86_vendor_id[16];
+ char x86_model_id[64];
+};
+
+extern bool cpu_has_feature(unsigned int feature);
+extern int cpu_init(void);
+extern int cpu_dump_cpuinfo(void);
+extern int cpu_validate_cpuinfo(void);
+extern int cpuinfo_dump(void);
+extern int cpuinfo_check(void);
+
+#endif /* __CR_CPU_H__ */
diff --git a/arch/x86/include/asm/dump.h b/arch/x86/include/asm/dump.h
new file mode 100644
index 000000000..1505fd298
--- /dev/null
+++ b/arch/x86/include/asm/dump.h
@@ -0,0 +1,11 @@
+#ifndef __CR_ASM_DUMP_H__
+#define __CR_ASM_DUMP_H__
+
+extern int get_task_regs(pid_t pid, user_regs_struct_t regs, CoreEntry *core);
+extern int arch_alloc_thread_info(CoreEntry *core);
+extern void arch_free_thread_info(CoreEntry *core);
+
+
+#define core_put_tls(core, tls)
+
+#endif
diff --git a/arch/x86/include/asm/fpu.h b/arch/x86/include/asm/fpu.h
new file mode 100644
index 000000000..be168324b
--- /dev/null
+++ b/arch/x86/include/asm/fpu.h
@@ -0,0 +1,102 @@
+#ifndef __CR_ASM_FPU_H__
+#define __CR_ASM_FPU_H__
+
+#include
+#include
+
+#include "compiler.h"
+#include "asm/int.h"
+
+#define FP_MIN_ALIGN_BYTES 64
+
+#define FP_XSTATE_MAGIC1 0x46505853U
+#define FP_XSTATE_MAGIC2 0x46505845U
+#define FP_XSTATE_MAGIC2_SIZE sizeof(FP_XSTATE_MAGIC2)
+
+#define XSTATE_FP 0x1
+#define XSTATE_SSE 0x2
+#define XSTATE_YMM 0x4
+
+#define FXSAVE_SIZE 512
+#define XSAVE_SIZE 832
+
+struct fpx_sw_bytes {
+ u32 magic1;
+ u32 extended_size;
+ u64 xstate_bv;
+ u32 xstate_size;
+ u32 padding[7];
+};
+
+struct i387_fxsave_struct {
+ u16 cwd; /* Control Word */
+ u16 swd; /* Status Word */
+ u16 twd; /* Tag Word */
+ u16 fop; /* Last Instruction Opcode */
+ union {
+ struct {
+ u64 rip; /* Instruction Pointer */
+ u64 rdp; /* Data Pointer */
+ };
+ struct {
+ u32 fip; /* FPU IP Offset */
+ u32 fcs; /* FPU IP Selector */
+ u32 foo; /* FPU Operand Offset */
+ u32 fos; /* FPU Operand Selector */
+ };
+ };
+ u32 mxcsr; /* MXCSR Register State */
+ u32 mxcsr_mask; /* MXCSR Mask */
+
+ /* 8*16 bytes for each FP-reg = 128 bytes */
+ u32 st_space[32];
+
+ /* 16*16 bytes for each XMM-reg = 256 bytes */
+ u32 xmm_space[64];
+
+ u32 padding[12];
+
+ union {
+ u32 padding1[12];
+ u32 sw_reserved[12];
+ };
+
+} __aligned(16);
+
+struct xsave_hdr_struct {
+ u64 xstate_bv;
+ u64 reserved1[2];
+ u64 reserved2[5];
+} __packed;
+
+struct ymmh_struct {
+ u32 ymmh_space[64];
+} __packed;
+
+/*
+ * cpu requires it to be 64 byte aligned
+ */
+struct xsave_struct {
+ struct i387_fxsave_struct i387;
+ struct xsave_hdr_struct xsave_hdr;
+ struct ymmh_struct ymmh;
+} __aligned(FP_MIN_ALIGN_BYTES) __packed;
+
+/*
+ * This one is used in restorer.
+ */
+typedef struct {
+ /*
+ * The FPU xsave area must be continious and FP_MIN_ALIGN_BYTES
+ * aligned, thus make sure the compiler won't insert any hole here.
+ */
+
+ union {
+ struct xsave_struct xsave;
+ unsigned char __pad[sizeof(struct xsave_struct) + FP_XSTATE_MAGIC2_SIZE];
+ };
+
+ bool has_fpu;
+} fpu_state_t;
+
+#endif /* __CR_ASM_FPU_H__ */
diff --git a/criu/arch/loongarch64/include/asm/int.h b/arch/x86/include/asm/int.h
similarity index 100%
rename from criu/arch/loongarch64/include/asm/int.h
rename to arch/x86/include/asm/int.h
diff --git a/arch/x86/include/asm/linkage.h b/arch/x86/include/asm/linkage.h
new file mode 100644
index 000000000..5e0948f07
--- /dev/null
+++ b/arch/x86/include/asm/linkage.h
@@ -0,0 +1,24 @@
+#ifndef __CR_LINKAGE_H__
+#define __CR_LINKAGE_H__
+
+#ifdef __ASSEMBLY__
+
+#define __ALIGN .align 4, 0x90
+#define __ALIGN_STR ".align 4, 0x90"
+
+#define GLOBAL(name) \
+ .globl name; \
+ name:
+
+#define ENTRY(name) \
+ .globl name; \
+ .type name, @function; \
+ __ALIGN; \
+ name:
+
+#define END(sym) \
+ .size sym, . - sym
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __CR_LINKAGE_H__ */
diff --git a/arch/x86/include/asm/parasite-syscall.h b/arch/x86/include/asm/parasite-syscall.h
new file mode 100644
index 000000000..4d56cb072
--- /dev/null
+++ b/arch/x86/include/asm/parasite-syscall.h
@@ -0,0 +1,20 @@
+#ifndef __CR_ASM_PARASITE_SYSCALL_H__
+#define __CR_ASM_PARASITE_SYSCALL_H__
+
+#include "asm/types.h"
+
+struct parasite_ctl;
+
+#define ARCH_SI_TRAP SI_KERNEL
+
+
+extern const char code_syscall[];
+extern const int code_syscall_size;
+
+void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *regs);
+
+void *mmap_seized(struct parasite_ctl *ctl,
+ void *addr, size_t length, int prot,
+ int flags, int fd, off_t offset);
+
+#endif
diff --git a/arch/x86/include/asm/parasite.h b/arch/x86/include/asm/parasite.h
new file mode 100644
index 000000000..48e48ac98
--- /dev/null
+++ b/arch/x86/include/asm/parasite.h
@@ -0,0 +1,6 @@
+#ifndef __ASM_PARASITE_H__
+#define __ASM_PARASITE_H__
+
+static inline void arch_get_tls(tls_t *ptls) { (void)ptls; }
+
+#endif
diff --git a/arch/x86/include/asm/prlimit.h b/arch/x86/include/asm/prlimit.h
new file mode 100644
index 000000000..6746ba0e6
--- /dev/null
+++ b/arch/x86/include/asm/prlimit.h
@@ -0,0 +1,14 @@
+#ifndef __CR_PRLIMIT_H__
+#define __CR_PRLIMIT_H__
+
+#include
+#include
+#include
+
+#include "config.h"
+
+#ifndef CONFIG_HAS_PRLIMIT
+extern int prlimit(pid_t pid, int resource, const struct rlimit *new_rlimit, struct rlimit *old_rlimit);
+#endif
+
+#endif /* __CR_PRLIMIT_H__ */
diff --git a/arch/x86/include/asm/processor-flags.h b/arch/x86/include/asm/processor-flags.h
new file mode 100644
index 000000000..9f1bccdbe
--- /dev/null
+++ b/arch/x86/include/asm/processor-flags.h
@@ -0,0 +1,28 @@
+#ifndef __CR_PROCESSOR_FLAGS_H__
+#define __CR_PROCESSOR_FLAGS_H__
+
+/* Taken from linux kernel headers */
+
+/*
+ * EFLAGS bits
+ */
+#define X86_EFLAGS_CF 0x00000001 /* Carry Flag */
+#define X86_EFLAGS_BIT1 0x00000002 /* Bit 1 - always on */
+#define X86_EFLAGS_PF 0x00000004 /* Parity Flag */
+#define X86_EFLAGS_AF 0x00000010 /* Auxiliary carry Flag */
+#define X86_EFLAGS_ZF 0x00000040 /* Zero Flag */
+#define X86_EFLAGS_SF 0x00000080 /* Sign Flag */
+#define X86_EFLAGS_TF 0x00000100 /* Trap Flag */
+#define X86_EFLAGS_IF 0x00000200 /* Interrupt Flag */
+#define X86_EFLAGS_DF 0x00000400 /* Direction Flag */
+#define X86_EFLAGS_OF 0x00000800 /* Overflow Flag */
+#define X86_EFLAGS_IOPL 0x00003000 /* IOPL mask */
+#define X86_EFLAGS_NT 0x00004000 /* Nested Task */
+#define X86_EFLAGS_RF 0x00010000 /* Resume Flag */
+#define X86_EFLAGS_VM 0x00020000 /* Virtual Mode */
+#define X86_EFLAGS_AC 0x00040000 /* Alignment Check */
+#define X86_EFLAGS_VIF 0x00080000 /* Virtual Interrupt Flag */
+#define X86_EFLAGS_VIP 0x00100000 /* Virtual Interrupt Pending */
+#define X86_EFLAGS_ID 0x00200000 /* CPUID detection flag */
+
+#endif /* __CR_PROCESSOR_FLAGS_H__ */
diff --git a/arch/x86/include/asm/restore.h b/arch/x86/include/asm/restore.h
new file mode 100644
index 000000000..7c39e0060
--- /dev/null
+++ b/arch/x86/include/asm/restore.h
@@ -0,0 +1,27 @@
+#ifndef __CR_ASM_RESTORE_H__
+#define __CR_ASM_RESTORE_H__
+
+#include "asm/restorer.h"
+
+#include "protobuf/core.pb-c.h"
+
+#define JUMP_TO_RESTORER_BLOB(new_sp, restore_task_exec_start, \
+ task_args) \
+ asm volatile( \
+ "movq %0, %%rbx \n" \
+ "movq %1, %%rax \n" \
+ "movq %2, %%rdi \n" \
+ "movq %%rbx, %%rsp \n" \
+ "callq *%%rax \n" \
+ : \
+ : "g"(new_sp), \
+ "g"(restore_task_exec_start), \
+ "g"(task_args) \
+ : "rsp", "rdi", "rsi", "rbx", "rax", "memory")
+
+#define core_get_tls(pcore, ptls)
+
+
+int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core);
+
+#endif
diff --git a/arch/x86/include/asm/restorer.h b/arch/x86/include/asm/restorer.h
new file mode 100644
index 000000000..70199fb86
--- /dev/null
+++ b/arch/x86/include/asm/restorer.h
@@ -0,0 +1,152 @@
+#ifndef __CR_ASM_RESTORER_H__
+#define __CR_ASM_RESTORER_H__
+
+#include "asm/types.h"
+#include "asm/fpu.h"
+#include "protobuf/core.pb-c.h"
+
+struct pt_regs {
+ unsigned long r15;
+ unsigned long r14;
+ unsigned long r13;
+ unsigned long r12;
+ unsigned long bp;
+ unsigned long bx;
+
+ unsigned long r11;
+ unsigned long r10;
+ unsigned long r9;
+ unsigned long r8;
+ unsigned long ax;
+ unsigned long cx;
+ unsigned long dx;
+ unsigned long si;
+ unsigned long di;
+ unsigned long orig_ax;
+
+ unsigned long ip;
+ unsigned long cs;
+ unsigned long flags;
+ unsigned long sp;
+ unsigned long ss;
+};
+
+struct rt_sigcontext {
+ unsigned long r8;
+ unsigned long r9;
+ unsigned long r10;
+ unsigned long r11;
+ unsigned long r12;
+ unsigned long r13;
+ unsigned long r14;
+ unsigned long r15;
+ unsigned long rdi;
+ unsigned long rsi;
+ unsigned long rbp;
+ unsigned long rbx;
+ unsigned long rdx;
+ unsigned long rax;
+ unsigned long rcx;
+ unsigned long rsp;
+ unsigned long rip;
+ unsigned long eflags;
+ unsigned short cs;
+ unsigned short gs;
+ unsigned short fs;
+ unsigned short __pad0;
+ unsigned long err;
+ unsigned long trapno;
+ unsigned long oldmask;
+ unsigned long cr2;
+ void *fpstate;
+ unsigned long reserved1[8];
+};
+
+#include "sigframe.h"
+
+struct rt_sigframe {
+ char *pretcode;
+ struct rt_ucontext uc;
+ struct rt_siginfo info;
+
+ fpu_state_t fpu_state;
+};
+
+
+#define ARCH_RT_SIGRETURN(new_sp) \
+ asm volatile( \
+ "movq %0, %%rax \n" \
+ "movq %%rax, %%rsp \n" \
+ "movl $"__stringify(__NR_rt_sigreturn)", %%eax \n" \
+ "syscall \n" \
+ : \
+ : "r"(new_sp) \
+ : "rax","rsp","memory")
+
+#define RUN_CLONE_RESTORE_FN(ret, clone_flags, new_sp, parent_tid, \
+ thread_args, clone_restore_fn) \
+ asm volatile( \
+ "clone_emul: \n" \
+ "movq %2, %%rsi \n" \
+ "subq $16, %%rsi \n" \
+ "movq %6, %%rdi \n" \
+ "movq %%rdi, 8(%%rsi) \n" \
+ "movq %5, %%rdi \n" \
+ "movq %%rdi, 0(%%rsi) \n" \
+ "movq %1, %%rdi \n" \
+ "movq %3, %%rdx \n" \
+ "movq %4, %%r10 \n" \
+ "movl $"__stringify(__NR_clone)", %%eax \n" \
+ "syscall \n" \
+ \
+ "testq %%rax,%%rax \n" \
+ "jz thread_run \n" \
+ \
+ "movq %%rax, %0 \n" \
+ "jmp clone_end \n" \
+ \
+ "thread_run: \n" \
+ "xorq %%rbp, %%rbp \n" \
+ "popq %%rax \n" \
+ "popq %%rdi \n" \
+ "callq *%%rax \n" \
+ \
+ "clone_end: \n" \
+ : "=r"(ret) \
+ : "g"(clone_flags), \
+ "g"(new_sp), \
+ "g"(&parent_tid), \
+ "g"(&thread_args[i].pid), \
+ "g"(clone_restore_fn), \
+ "g"(&thread_args[i]) \
+ : "rax", "rdi", "rsi", "rdx", "r10", "memory")
+
+#define ARCH_FAIL_CORE_RESTORE \
+ asm volatile( \
+ "movq %0, %%rsp \n" \
+ "movq 0, %%rax \n" \
+ "jmp *%%rax \n" \
+ : \
+ : "r"(ret) \
+ : "memory")
+
+#define RT_SIGFRAME_UC(rt_sigframe) rt_sigframe->uc
+#define RT_SIGFRAME_REGIP(rt_sigframe) (rt_sigframe)->uc.uc_mcontext.rip
+#define RT_SIGFRAME_HAS_FPU(rt_sigframe) (rt_sigframe)->fpu_state.has_fpu
+#define RT_SIGFRAME_FPU(rt_sigframe) (rt_sigframe)->fpu_state
+
+#define SIGFRAME_OFFSET 8
+
+
+int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r);
+int restore_nonsigframe_gpregs(UserX86RegsEntry *r);
+
+int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe, fpu_state_t *fpu_state);
+
+static inline void restore_tls(tls_t *ptls) { (void)ptls; }
+
+int ptrace_set_breakpoint(pid_t pid, void *addr);
+int ptrace_flush_breakpoints(pid_t pid);
+
+
+#endif
diff --git a/arch/x86/include/asm/string.h b/arch/x86/include/asm/string.h
new file mode 100644
index 000000000..e1d875e45
--- /dev/null
+++ b/arch/x86/include/asm/string.h
@@ -0,0 +1,24 @@
+#ifndef __CR_ASM_STRING_H__
+#define __CR_ASM_STRING_H__
+
+#define HAS_BUILTIN_MEMCPY
+
+#include "compiler.h"
+#include "asm-generic/string.h"
+
+static always_inline void *builtin_memcpy(void *to, const void *from, unsigned int n)
+{
+ int d0, d1, d2;
+ asm volatile("rep ; movsl \n"
+ "movl %4,%%ecx \n"
+ "andl $3,%%ecx \n"
+ "jz 1f \n"
+ "rep ; movsb \n"
+ "1:"
+ : "=&c" (d0), "=&D" (d1), "=&S" (d2)
+ : "0" (n / 4), "g" (n), "1" ((long)to), "2" ((long)from)
+ : "memory");
+ return to;
+}
+
+#endif /* __CR_ASM_STRING_H__ */
diff --git a/arch/x86/include/asm/types.h b/arch/x86/include/asm/types.h
new file mode 100644
index 000000000..d7e869a50
--- /dev/null
+++ b/arch/x86/include/asm/types.h
@@ -0,0 +1,132 @@
+#ifndef __CR_ASM_TYPES_H__
+#define __CR_ASM_TYPES_H__
+
+#include
+#include
+
+#include "asm-generic/page.h"
+#include "asm/bitops.h"
+#include "asm/int.h"
+#include "asm/prlimit.h"
+
+#include "protobuf/core.pb-c.h"
+
+#define SIGMAX 64
+#define SIGMAX_OLD 31
+
+#define MAJOR(dev) ((dev)>>8)
+#define MINOR(dev) ((dev) & 0xff)
+
+typedef void rt_signalfn_t(int, siginfo_t *, void *);
+typedef rt_signalfn_t *rt_sighandler_t;
+
+typedef void rt_restorefn_t(void);
+typedef rt_restorefn_t *rt_sigrestore_t;
+
+#define _KNSIG 64
+# define _NSIG_BPW 64
+
+#define _KNSIG_WORDS (_KNSIG / _NSIG_BPW)
+
+typedef struct {
+ unsigned long sig[_KNSIG_WORDS];
+} k_rtsigset_t;
+
+static inline void ksigfillset(k_rtsigset_t *set)
+{
+ int i;
+ for (i = 0; i < _KNSIG_WORDS; i++)
+ set->sig[i] = (unsigned long)-1;
+}
+
+#define SA_RESTORER 0x04000000
+
+typedef struct {
+ rt_sighandler_t rt_sa_handler;
+ unsigned long rt_sa_flags;
+ rt_sigrestore_t rt_sa_restorer;
+ k_rtsigset_t rt_sa_mask;
+} rt_sigaction_t;
+
+typedef struct {
+ unsigned int entry_number;
+ unsigned int base_addr;
+ unsigned int limit;
+ unsigned int seg_32bit:1;
+ unsigned int contents:2;
+ unsigned int read_exec_only:1;
+ unsigned int limit_in_pages:1;
+ unsigned int seg_not_present:1;
+ unsigned int useable:1;
+ unsigned int lm:1;
+} user_desc_t;
+
+typedef struct {
+ unsigned long r15;
+ unsigned long r14;
+ unsigned long r13;
+ unsigned long r12;
+ unsigned long bp;
+ unsigned long bx;
+ unsigned long r11;
+ unsigned long r10;
+ unsigned long r9;
+ unsigned long r8;
+ unsigned long ax;
+ unsigned long cx;
+ unsigned long dx;
+ unsigned long si;
+ unsigned long di;
+ unsigned long orig_ax;
+ unsigned long ip;
+ unsigned long cs;
+ unsigned long flags;
+ unsigned long sp;
+ unsigned long ss;
+ unsigned long fs_base;
+ unsigned long gs_base;
+ unsigned long ds;
+ unsigned long es;
+ unsigned long fs;
+ unsigned long gs;
+} user_regs_struct_t;
+
+typedef struct {
+ unsigned short cwd;
+ unsigned short swd;
+ unsigned short twd; /* Note this is not the same as
+ the 32bit/x87/FSAVE twd */
+ unsigned short fop;
+ u64 rip;
+ u64 rdp;
+ u32 mxcsr;
+ u32 mxcsr_mask;
+ u32 st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */
+ u32 xmm_space[64]; /* 16*16 bytes for each XMM-reg = 256 bytes */
+ u32 padding[24];
+} user_fpregs_struct_t;
+
+#define ASSIGN_TYPED(a, b) do { a = (typeof(a))b; } while (0)
+#define ASSIGN_MEMBER(a,b,m) do { ASSIGN_TYPED((a)->m, (b)->m); } while (0)
+
+#define TASK_SIZE ((1UL << 47) - PAGE_SIZE)
+
+typedef u64 auxv_t;
+typedef u32 tls_t;
+
+#define REG_RES(regs) ((regs).ax)
+#define REG_IP(regs) ((regs).ip)
+#define REG_SYSCALL_NR(regs) ((regs).orig_ax)
+
+#define CORE_ENTRY__MARCH CORE_ENTRY__MARCH__X86_64
+
+#define AT_VECTOR_SIZE 44
+
+#define CORE_THREAD_ARCH_INFO(core) core->thread_info
+
+typedef UserX86RegsEntry UserRegsEntry;
+
+static inline u64 encode_pointer(void *p) { return (u64)p; }
+static inline void *decode_pointer(u64 v) { return (void*)v; }
+
+#endif /* __CR_ASM_TYPES_H__ */
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
new file mode 100644
index 000000000..56761fa3e
--- /dev/null
+++ b/arch/x86/include/asm/vdso.h
@@ -0,0 +1,159 @@
+#ifndef __CR_ASM_VDSO_H__
+#define __CR_ASM_VDSO_H__
+
+#include
+
+#include "asm/int.h"
+#include "protobuf/vma.pb-c.h"
+
+struct parasite_ctl;
+struct vm_area_list;
+
+#define VDSO_PROT (PROT_READ | PROT_EXEC)
+#define VVAR_PROT (PROT_READ)
+
+#define VDSO_BAD_ADDR (-1ul)
+#define VVAR_BAD_ADDR VDSO_BAD_ADDR
+#define VDSO_BAD_PFN (-1ull)
+#define VVAR_BAD_PFN VDSO_BAD_PFN
+
+struct vdso_symbol {
+ char name[32];
+ unsigned long offset;
+};
+
+#define VDSO_SYMBOL_INIT { .offset = VDSO_BAD_ADDR, }
+
+/* Check if symbol present in symtable */
+static inline bool vdso_symbol_empty(struct vdso_symbol *s)
+{
+ return s->offset == VDSO_BAD_ADDR && s->name[0] == '\0';
+}
+
+/*
+ * This is a minimal amount of symbols
+ * we should support at the moment.
+ */
+enum {
+ VDSO_SYMBOL_CLOCK_GETTIME,
+ VDSO_SYMBOL_GETCPU,
+ VDSO_SYMBOL_GETTIMEOFDAY,
+ VDSO_SYMBOL_TIME,
+
+ VDSO_SYMBOL_MAX
+};
+
+struct vdso_symtable {
+ unsigned long vma_start;
+ unsigned long vma_end;
+ unsigned long vvar_start;
+ unsigned long vvar_end;
+ struct vdso_symbol symbols[VDSO_SYMBOL_MAX];
+};
+
+#define VDSO_SYMTABLE_INIT \
+ { \
+ .vma_start = VDSO_BAD_ADDR, \
+ .vma_end = VDSO_BAD_ADDR, \
+ .vvar_start = VVAR_BAD_ADDR, \
+ .vvar_end = VVAR_BAD_ADDR, \
+ .symbols = { \
+ [0 ... VDSO_SYMBOL_MAX - 1] = \
+ (struct vdso_symbol)VDSO_SYMBOL_INIT, \
+ }, \
+ }
+
+/* Size of VMA associated with vdso */
+static inline unsigned long vdso_vma_size(struct vdso_symtable *t)
+{
+ return t->vma_end - t->vma_start;
+}
+
+static inline unsigned long vvar_vma_size(struct vdso_symtable *t)
+{
+ return t->vvar_end - t->vvar_start;
+}
+/*
+ * Special mark which allows to identify runtime vdso where
+ * calls from proxy vdso are redirected. This mark usually
+ * placed at the start of vdso area where Elf header lives.
+ * Since such runtime vdso is solevey used by proxy and
+ * nobody else is supposed to access it, it's more-less
+ * safe to screw the Elf header with @signature and
+ * @proxy_addr.
+ *
+ * The @proxy_addr deserves a few comments. When we redirect
+ * the calls from proxy to runtime vdso, on next checkpoint
+ * it won't be possible to find which VMA is proxy, thus
+ * we save its address in the member.
+ */
+struct vdso_mark {
+ u64 signature;
+ unsigned long proxy_vdso_addr;
+
+ unsigned long version;
+
+ /*
+ * In case of new vDSO format the VVAR area address
+ * neeed for easier discovering where it lives without
+ * relying on procfs output.
+ */
+ unsigned long proxy_vvar_addr;
+};
+
+#define VDSO_MARK_SIGNATURE (0x6f73647675697263ULL) /* Magic number (criuvdso) */
+#define VDSO_MARK_SIGNATURE_V2 (0x4f53447675697263ULL) /* Magic number (criuvDSO) */
+#define VDSO_MARK_CUR_VERSION (2)
+
+static inline void vdso_put_mark(void *where, unsigned long proxy_vdso_addr, unsigned long proxy_vvar_addr)
+{
+ struct vdso_mark *m = where;
+
+ m->signature = VDSO_MARK_SIGNATURE_V2;
+ m->proxy_vdso_addr = proxy_vdso_addr;
+ m->version = VDSO_MARK_CUR_VERSION;
+ m->proxy_vvar_addr = proxy_vvar_addr;
+}
+
+static inline bool is_vdso_mark(void *addr)
+{
+ struct vdso_mark *m = addr;
+
+ if (m->signature == VDSO_MARK_SIGNATURE_V2) {
+ /*
+ * New format
+ */
+ return true;
+ } else if (m->signature == VDSO_MARK_SIGNATURE) {
+ /*
+ * Old format -- simply extend the mark up
+ * to the version we support.
+ */
+ vdso_put_mark(m, m->proxy_vdso_addr, VVAR_BAD_ADDR);
+ return true;
+ }
+ return false;
+}
+
+#define VDSO_SYMBOL_CLOCK_GETTIME_NAME "__vdso_clock_gettime"
+#define VDSO_SYMBOL_GETCPU_NAME "__vdso_getcpu"
+#define VDSO_SYMBOL_GETTIMEOFDAY_NAME "__vdso_gettimeofday"
+#define VDSO_SYMBOL_TIME_NAME "__vdso_time"
+
+
+
+extern struct vdso_symtable vdso_sym_rt;
+extern u64 vdso_pfn;
+
+extern int vdso_init(void);
+extern int vdso_do_park(struct vdso_symtable *sym_rt, unsigned long park_at, unsigned long park_size);
+extern int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t);
+extern int vdso_proxify(char *who, struct vdso_symtable *sym_rt,
+ unsigned long vdso_rt_parked_at, size_t index,
+ VmaEntry *vmas, size_t nr_vmas);
+
+extern int vdso_redirect_calls(void *base_to, void *base_from, struct vdso_symtable *to, struct vdso_symtable *from);
+extern int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
+ struct vm_area_list *vma_area_list);
+
+#endif /* __CR_ASM_VDSO_H__ */
diff --git a/arch/x86/parasite-head.S b/arch/x86/parasite-head.S
new file mode 100644
index 000000000..8caf9b3b9
--- /dev/null
+++ b/arch/x86/parasite-head.S
@@ -0,0 +1,17 @@
+#include "asm/linkage.h"
+#include "parasite.h"
+
+ .section .head.text, "ax"
+ENTRY(__export_parasite_head_start)
+ subq $16, %rsp
+ andq $~15, %rsp
+ pushq $0
+ movq %rsp, %rbp
+ movl __export_parasite_cmd(%rip), %edi
+ leaq __export_parasite_args(%rip), %rsi
+ call parasite_service
+ int $0x03
+ .align 8
+__export_parasite_cmd:
+ .long 0
+END(__export_parasite_head_start)
diff --git a/arch/x86/prlimit.c b/arch/x86/prlimit.c
new file mode 100644
index 000000000..596d6da20
--- /dev/null
+++ b/arch/x86/prlimit.c
@@ -0,0 +1,69 @@
+#include
+#include
+#include
+#include
+
+#include "asm/types.h"
+#include "asm/prlimit.h"
+
+#include "compiler.h"
+#include "syscall.h"
+#include "config.h"
+
+#ifndef CONFIG_HAS_PRLIMIT
+
+#ifndef RLIM64_INFINITY
+# define RLIM64_INFINITY (~0ULL)
+#endif
+
+int prlimit(pid_t pid, int resource, const struct rlimit *new_rlimit, struct rlimit *old_rlimit)
+{
+ struct rlimit64 new_rlimit64_mem;
+ struct rlimit64 old_rlimit64_mem;
+ struct rlimit64 *new_rlimit64 = NULL;
+ struct rlimit64 *old_rlimit64 = NULL;
+ int ret;
+
+ if (old_rlimit)
+ old_rlimit64 = &old_rlimit64_mem;
+
+ if (new_rlimit) {
+ if (new_rlimit->rlim_cur == RLIM_INFINITY)
+ new_rlimit64_mem.rlim_cur = RLIM64_INFINITY;
+ else
+ new_rlimit64_mem.rlim_cur = new_rlimit->rlim_cur;
+ if (new_rlimit->rlim_max == RLIM_INFINITY)
+ new_rlimit64_mem.rlim_max = RLIM64_INFINITY;
+ else
+ new_rlimit64_mem.rlim_max = new_rlimit->rlim_max;
+ new_rlimit64 = &new_rlimit64_mem;
+ }
+
+ ret = sys_prlimit64(pid, resource, new_rlimit64, old_rlimit64);
+
+ if (ret == 0 && old_rlimit) {
+ old_rlimit->rlim_cur = old_rlimit64_mem.rlim_cur;
+ if (old_rlimit->rlim_cur != old_rlimit64_mem.rlim_cur) {
+ if (new_rlimit) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ old_rlimit->rlim_cur = RLIM_INFINITY;
+ }
+ old_rlimit->rlim_max = old_rlimit64_mem.rlim_max;
+ if (old_rlimit->rlim_max != old_rlimit64_mem.rlim_max) {
+ if (new_rlimit) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ old_rlimit->rlim_max = RLIM_INFINITY;
+ }
+ } else if (ret) {
+ errno = -ret;
+ ret = -1;
+ }
+
+ return ret;
+}
+
+#endif /* CONFIG_HAS_PRLIMIT */
diff --git a/arch/x86/restorer.c b/arch/x86/restorer.c
new file mode 100644
index 000000000..8dc5ac452
--- /dev/null
+++ b/arch/x86/restorer.c
@@ -0,0 +1,32 @@
+#include
+#include
+
+#include "restorer.h"
+#include "asm/restorer.h"
+#include "asm/fpu.h"
+
+#include "syscall.h"
+#include "log.h"
+#include "cpu.h"
+
+int restore_nonsigframe_gpregs(UserX86RegsEntry *r)
+{
+ long ret;
+ unsigned long fsgs_base;
+
+ fsgs_base = r->fs_base;
+ ret = sys_arch_prctl(ARCH_SET_FS, fsgs_base);
+ if (ret) {
+ pr_info("SET_FS fail %ld\n", ret);
+ return -1;
+ }
+
+ fsgs_base = r->gs_base;
+ ret = sys_arch_prctl(ARCH_SET_GS, fsgs_base);
+ if (ret) {
+ pr_info("SET_GS fail %ld\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/compel/arch/x86/plugins/std/syscalls/syscall-common-x86-64.S b/arch/x86/syscall-common-x86-64.S
similarity index 90%
rename from compel/arch/x86/plugins/std/syscalls/syscall-common-x86-64.S
rename to arch/x86/syscall-common-x86-64.S
index 74465c302..b93c31288 100644
--- a/compel/arch/x86/plugins/std/syscalls/syscall-common-x86-64.S
+++ b/arch/x86/syscall-common-x86-64.S
@@ -1,4 +1,4 @@
-#include "common/asm/linkage.h"
+#include "asm/linkage.h"
#define SYSCALL(name, opcode) \
ENTRY(name); \
diff --git a/arch/x86/syscall-x86-64.def b/arch/x86/syscall-x86-64.def
new file mode 100644
index 000000000..b793e8e1e
--- /dev/null
+++ b/arch/x86/syscall-x86-64.def
@@ -0,0 +1,103 @@
+#
+# System calls table, please make sure the table consist only the syscalls
+# really used somewhere in project.
+#
+# The template is (name and srguments are optinal if you need only __NR_x
+# defined, but no realy entry point in syscalls lib).
+#
+# name code name arguments
+# -----------------------------------------------------------------------
+#
+__NR_read 0 sys_read (int fd, void *buf, unsigned long count)
+__NR_write 1 sys_write (int fd, const void *buf, unsigned long count)
+__NR_open 2 sys_open (const char *filename, unsigned long flags, unsigned long mode)
+__NR_close 3 sys_close (int fd)
+__NR_lseek 8 sys_lseek (int fd, unsigned long offset, unsigned long origin)
+__NR_mmap 9 sys_mmap (void *addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long offset)
+__NR_mprotect 10 sys_mprotect (const void *addr, unsigned long len, unsigned long prot)
+__NR_munmap 11 sys_munmap (void *addr, unsigned long len)
+__NR_brk 12 sys_brk (void *addr)
+__NR_rt_sigaction 13 sys_sigaction (int signum, const rt_sigaction_t *act, rt_sigaction_t *oldact, size_t sigsetsize)
+__NR_rt_sigprocmask 14 sys_sigprocmask (int how, k_rtsigset_t *set, k_rtsigset_t *old, size_t sigsetsize)
+__NR_rt_sigreturn 15 sys_rt_sigreturn (void)
+__NR_ioctl 16 sys_ioctl (unsigned int fd, unsigned int cmd, unsigned long arg)
+__NR_pread64 17 sys_pread (unsigned int fd, char *buf, size_t count, loff_t pos)
+__NR_mremap 25 sys_mremap (unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr)
+__NR_mincore 27 sys_mincore (void *addr, unsigned long size, unsigned char *vec)
+__NR_madvise 28 sys_madvise (unsigned long start, size_t len, int behavior)
+__NR_shmat 30 sys_shmat (int shmid, void *shmaddr, int shmflag)
+__NR_pause 34 sys_pause (void)
+__NR_nanosleep 35 sys_nanosleep (struct timespec *req, struct timespec *rem)
+__NR_getitimer 36 sys_getitimer (int which, const struct itimerval *val)
+__NR_setitimer 38 sys_setitimer (int which, const struct itimerval *val, struct itimerval *old)
+__NR_getpid 39 sys_getpid (void)
+__NR_socket 41 sys_socket (int domain, int type, int protocol)
+__NR_connect 42 sys_connect (int sockfd, struct sockaddr *addr, int addrlen)
+__NR_sendto 44 sys_sendto (int sockfd, void *buff, size_t len, unsigned int flags, struct sockaddr *addr, int addr_len)
+__NR_recvfrom 45 sys_recvfrom (int sockfd, void *ubuf, size_t size, unsigned int flags, struct sockaddr *addr, int *addr_len)
+__NR_sendmsg 46 sys_sendmsg (int sockfd, const struct msghdr *msg, int flags)
+__NR_recvmsg 47 sys_recvmsg (int sockfd, struct msghdr *msg, int flags)
+__NR_shutdown 48 sys_shutdown (int sockfd, int how)
+__NR_bind 49 sys_bind (int sockfd, const struct sockaddr *addr, int addrlen)
+__NR_setsockopt 54 sys_setsockopt (int sockfd, int level, int optname, const void *optval, socklen_t optlen)
+__NR_getsockopt 55 sys_getsockopt (int sockfd, int level, int optname, const void *optval, socklen_t *optlen)
+__NR_clone 56 sys_clone (unsigned long flags, void *child_stack, void *parent_tid, void *child_tid)
+__NR_exit 60 sys_exit (unsigned long error_code)
+__NR_wait4 61 sys_wait4 (int pid, int *status, int options, struct rusage *ru)
+__NR_kill 62 sys_kill (long pid, int sig)
+__NR_fcntl 72 sys_fcntl (int fd, int type, long arg)
+__NR_flock 73 sys_flock (int fd, unsigned long cmd)
+__NR_mkdir 83 sys_mkdir (const char *name, int mode)
+__NR_rmdir 84 sys_rmdir (const char *name)
+__NR_unlink 87 sys_unlink (char *pathname)
+__NR_readlink 89 sys_readlink (const char *path, char *buf, int bufsize)
+__NR_umask 95 sys_umask (int mask)
+__NR_getgroups 115 sys_getgroups (int gsize, unsigned int *groups)
+__NR_setresuid 117 sys_setresuid (int uid, int euid, int suid)
+__NR_getresuid 118 sys_getresuid (int *uid, int *euid, int *suid)
+__NR_setresgid 119 sys_setresgid (int gid, int egid, int sgid)
+__NR_getresgid 120 sys_getresgid (int *gid, int *egid, int *sgid)
+__NR_getpgid 121 sys_getpgid (pid_t pid)
+__NR_setfsuid 122 sys_setfsuid (int fsuid)
+__NR_setfsgid 123 sys_setfsgid (int fsgid)
+__NR_getsid 124 sys_getsid (void)
+__NR_capget 125 sys_capget (struct cap_header *h, struct cap_data *d)
+__NR_capset 126 sys_capset (struct cap_header *h, struct cap_data *d)
+__NR_rt_sigqueueinfo 129 sys_rt_sigqueueinfo (pid_t pid, int sig, siginfo_t *info)
+__NR_sigaltstack 131 sys_sigaltstack (const void *uss, void *uoss)
+__NR_personality 135 sys_personality (unsigned int personality)
+__NR_setpriority 141 sys_setpriority (int which, int who, int nice)
+__NR_sched_setscheduler 144 sys_sched_setscheduler (int pid, int policy, struct sched_param *p)
+__NR_prctl 157 sys_prctl (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
+__NR_arch_prctl 158 sys_arch_prctl (int option, unsigned long addr)
+__NR_setrlimit 160 sys_setrlimit (int resource, struct krlimit *rlim)
+__NR_mount 165 sys_mount (char *dev_nmae, char *dir_name, char *type, unsigned long flags, void *data)
+__NR_umount2 166 sys_umount2 (char *name, int flags)
+__NR_gettid 186 sys_gettid (void)
+__NR_futex 202 sys_futex (u32 *uaddr, int op, u32 val, struct timespec *utime, u32 *uaddr2, u32 val3)
+__NR_set_thread_area 205 sys_set_thread_area (user_desc_t *info)
+__NR_get_thread_area 211 sys_get_thread_area (user_desc_t *info)
+__NR_set_tid_address 218 sys_set_tid_address (int *tid_addr)
+__NR_restart_syscall 219 sys_restart_syscall (void)
+__NR_sys_timer_create 222 sys_timer_create (clockid_t which_clock, struct sigevent *timer_event_spec, timer_t *created_timer_id)
+__NR_sys_timer_settime 223 sys_timer_settime (timer_t timer_id, int flags, const struct itimerspec *new_setting, struct itimerspec *old_setting)
+__NR_sys_timer_gettime 224 sys_timer_gettime (int timer_id, const struct itimerspec *setting)
+__NR_sys_timer_getoverrun 225 sys_timer_getoverrun (int timer_id)
+__NR_sys_timer_delete 226 sys_timer_delete (timer_t timer_id)
+__NR_clock_gettime 228 sys_clock_gettime (const clockid_t which_clock, const struct timespec *tp)
+__NR_exit_group 231 sys_exit_group (int error_code)
+__NR_io_setup 206 sys_io_setup (unsigned nr_events, aio_context_t *ctx)
+__NR_io_getevents 208 sys_io_getevents (aio_context_t ctx, long min_nr, long nr, struct io_event *evs, struct timespec *tmo)
+__NR_set_robust_list 273 sys_set_robust_list (struct robust_list_head *head, size_t len)
+__NR_get_robust_list 274 sys_get_robust_list (int pid, struct robust_list_head **head_ptr, size_t *len_ptr)
+__NR_vmsplice 278 sys_vmsplice (int fd, const struct iovec *iov, unsigned long nr_segs, unsigned int flags)
+__NR_timerfd_settime 286 sys_timerfd_settime (int ufd, int flags, const struct itimerspec *utmr, struct itimerspec *otmr)
+__NR_signalfd4 289 sys_signalfd (int fd, k_rtsigset_t *mask, size_t sizemask, int flags)
+__NR_rt_tgsigqueueinfo 297 sys_rt_tgsigqueueinfo (pid_t tgid, pid_t pid, int sig, siginfo_t *info)
+__NR_fanotify_init 300 sys_fanotify_init (unsigned int flags, unsigned int event_f_flags)
+__NR_fanotify_mark 301 sys_fanotify_mark (int fanotify_fd, unsigned int flags, u64 mask, int dfd, const char *pathname)
+__NR_prlimit64 302 sys_prlimit64 (pid_t pid, unsigned int resource, const struct rlimit64 *new_rlim, struct rlimit64 *old_rlim)
+__NR_open_by_handle_at 304 sys_open_by_handle_at (int mountdirfd, struct file_handle *handle, int flags)
+__NR_setns 308 sys_setns (int fd, int nstype)
+__NR_kcmp 312 sys_kcmp (pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2)
+__NR_memfd_create 319 sys_memfd_create (const char *name, unsigned int flags)
diff --git a/arch/x86/syscalls-x86-64.sh b/arch/x86/syscalls-x86-64.sh
new file mode 100644
index 000000000..22c81293d
--- /dev/null
+++ b/arch/x86/syscalls-x86-64.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+gen_asm() {
+ in=$1
+ codesout=$2
+ codesinc=`echo $2 | sed -e 's/.*include\///g'`
+ protosout=$3
+ asmout=$4
+ asmcommon=`echo $5 | sed -e 's/.*include\///g'`
+ prototypes=`echo $6 | sed -e 's/.*include\///g'`
+
+ codesdef=`echo $codesout | sed -e 's/.*include\///g' | tr "[[:space:]].-" _`
+ protosdef=`echo $protosout | sed -e 's/.*include\///g' | tr "[[:space:]].-" _`
+
+ echo "/* Autogenerated, don't edit */" > $codesout
+ echo "#ifndef $codesdef" >> $codesout
+ echo "#define $codesdef" >> $codesout
+
+ echo "/* Autogenerated, don't edit */" > $protosout
+ echo "#ifndef $protosdef" >> $protosout
+ echo "#define $protosdef" >> $protosout
+ echo "#include \"$prototypes\"" >> $protosout
+ echo "#include \"$codesinc\"" >> $protosout
+
+ echo "/* Autogenerated, don't edit */" > $asmout
+ echo "#include \"$codesinc\"" >> $asmout
+ echo "#include \"$asmcommon\"" >> $asmout
+
+ cat $in | egrep -v '^#' | sed -e 's/\t\{1,\}/|/g' | awk -F '|' '{print "#define", $1, $2}' >> $codesout
+ cat $in | egrep -v '^#' | sed -e 's/\t\{1,\}/|/g' | awk -F '|' '{print "extern long ", $3, $4, ";"}' >> $protosout
+ cat $in | egrep -v '^#' | sed -e 's/\t\{1,\}/|/g' | awk -F '|' '{print "SYSCALL(", $3, ",", $2, ")"}' >> $asmout
+
+ echo "#endif /* $codesdef */" >> $codesout
+ echo "#endif /* $protosdef */" >> $protosout
+}
+
+gen_exec() {
+ in=$1
+ codecout=$2
+
+ echo "/* Autogenerated, don't edit */" > $codecout
+
+ cat $in | egrep -v '^#' | sed -e 's/\t\{1,\}/|/g' | awk -F '|' '{print "SYSCALL(", substr($3, 5), ",", $2, ")"}' >> $codecout
+}
+
+if [ "$1" = "--asm" ]; then
+ shift
+ gen_asm $@
+fi
+
+if [ "$1" = "--exec" ]; then
+ shift
+ gen_exec $@
+fi
diff --git a/arch/x86/vdso-pie.c b/arch/x86/vdso-pie.c
new file mode 100644
index 000000000..da86a5bc6
--- /dev/null
+++ b/arch/x86/vdso-pie.c
@@ -0,0 +1,440 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "asm/string.h"
+#include "asm/types.h"
+
+#include "compiler.h"
+#include "syscall.h"
+#include "image.h"
+#include "vdso.h"
+#include "vma.h"
+#include "log.h"
+#include "bug.h"
+
+#ifdef LOG_PREFIX
+# undef LOG_PREFIX
+#endif
+#define LOG_PREFIX "vdso: "
+
+typedef struct {
+ u16 movabs;
+ u64 imm64;
+ u16 jmp_rax;
+ u32 guards;
+} __packed jmp_t;
+
+int vdso_redirect_calls(void *base_to, void *base_from,
+ struct vdso_symtable *to,
+ struct vdso_symtable *from)
+{
+ jmp_t jmp = {
+ .movabs = 0xb848,
+ .jmp_rax = 0xe0ff,
+ .guards = 0xcccccccc,
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(to->symbols); i++) {
+ if (vdso_symbol_empty(&from->symbols[i]))
+ continue;
+
+ pr_debug("jmp: %lx/%lx -> %lx/%lx (index %d)\n",
+ (unsigned long)base_from, from->symbols[i].offset,
+ (unsigned long)base_to, to->symbols[i].offset, i);
+
+ jmp.imm64 = (unsigned long)base_to + to->symbols[i].offset;
+ builtin_memcpy((void *)(base_from + from->symbols[i].offset), &jmp, sizeof(jmp));
+ }
+
+ return 0;
+}
+
+
+/* Check if pointer is out-of-bound */
+static bool __ptr_oob(void *ptr, void *start, size_t size)
+{
+ void *end = (void *)((unsigned long)start + size);
+ return ptr > end || ptr < start;
+}
+
+/*
+ * Elf hash, see format specification.
+ */
+static unsigned long elf_hash(const unsigned char *name)
+{
+ unsigned long h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000ul;
+ if (g)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+int vdso_fill_symtable(char *mem, size_t size, struct vdso_symtable *t)
+{
+ Elf64_Phdr *dynamic = NULL, *load = NULL;
+ Elf64_Ehdr *ehdr = (void *)mem;
+ Elf64_Dyn *dyn_strtab = NULL;
+ Elf64_Dyn *dyn_symtab = NULL;
+ Elf64_Dyn *dyn_strsz = NULL;
+ Elf64_Dyn *dyn_syment = NULL;
+ Elf64_Dyn *dyn_hash = NULL;
+ Elf64_Word *hash = NULL;
+ Elf64_Phdr *phdr;
+ Elf64_Dyn *d;
+
+ Elf64_Word *bucket, *chain;
+ Elf64_Word nbucket, nchain;
+
+ /*
+ * See Elf specification for this magic values.
+ */
+ const char elf_ident[] = {
+ 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ const char *vdso_symbols[VDSO_SYMBOL_MAX] = {
+ [VDSO_SYMBOL_CLOCK_GETTIME] = VDSO_SYMBOL_CLOCK_GETTIME_NAME,
+ [VDSO_SYMBOL_GETCPU] = VDSO_SYMBOL_GETCPU_NAME,
+ [VDSO_SYMBOL_GETTIMEOFDAY] = VDSO_SYMBOL_GETTIMEOFDAY_NAME,
+ [VDSO_SYMBOL_TIME] = VDSO_SYMBOL_TIME_NAME,
+ };
+
+ char *dynsymbol_names;
+ unsigned int i, j, k;
+
+ BUILD_BUG_ON(sizeof(elf_ident) != sizeof(ehdr->e_ident));
+
+ pr_debug("Parsing at %lx %lx\n", (long)mem, (long)mem + (long)size);
+
+ /*
+ * Make sure it's a file we support.
+ */
+ if (builtin_memcmp(ehdr->e_ident, elf_ident, sizeof(elf_ident))) {
+ pr_err("Elf header magic mismatch\n");
+ return -EINVAL;
+ }
+
+ /*
+ * We need PT_LOAD and PT_DYNAMIC here. Each once.
+ */
+ phdr = (void *)&mem[ehdr->e_phoff];
+ for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+ if (__ptr_oob(phdr, mem, size))
+ goto err_oob;
+ switch (phdr->p_type) {
+ case PT_DYNAMIC:
+ if (dynamic) {
+ pr_err("Second PT_DYNAMIC header\n");
+ return -EINVAL;
+ }
+ dynamic = phdr;
+ break;
+ case PT_LOAD:
+ if (load) {
+ pr_err("Second PT_LOAD header\n");
+ return -EINVAL;
+ }
+ load = phdr;
+ break;
+ }
+ }
+
+ if (!load || !dynamic) {
+ pr_err("One of obligated program headers is missed\n");
+ return -EINVAL;
+ }
+
+ pr_debug("PT_LOAD p_vaddr: %lx\n", (unsigned long)load->p_vaddr);
+
+ /*
+ * Dynamic section tags should provide us the rest of information
+ * needed. Note that we're interested in a small set of tags.
+ */
+ d = (void *)&mem[dynamic->p_offset];
+ for (i = 0; i < dynamic->p_filesz / sizeof(*d); i++, d++) {
+ if (__ptr_oob(d, mem, size))
+ goto err_oob;
+
+ if (d->d_tag == DT_NULL) {
+ break;
+ } else if (d->d_tag == DT_STRTAB) {
+ dyn_strtab = d;
+ pr_debug("DT_STRTAB: %p\n", (void *)d->d_un.d_ptr);
+ } else if (d->d_tag == DT_SYMTAB) {
+ dyn_symtab = d;
+ pr_debug("DT_SYMTAB: %p\n", (void *)d->d_un.d_ptr);
+ } else if (d->d_tag == DT_STRSZ) {
+ dyn_strsz = d;
+ pr_debug("DT_STRSZ: %lu\n", (unsigned long)d->d_un.d_val);
+ } else if (d->d_tag == DT_SYMENT) {
+ dyn_syment = d;
+ pr_debug("DT_SYMENT: %lu\n", (unsigned long)d->d_un.d_val);
+ } else if (d->d_tag == DT_HASH) {
+ dyn_hash = d;
+ pr_debug("DT_HASH: %p\n", (void *)d->d_un.d_ptr);
+ }
+ }
+
+ if (!dyn_strtab || !dyn_symtab || !dyn_strsz || !dyn_syment || !dyn_hash) {
+ pr_err("Not all dynamic entries are present\n");
+ return -EINVAL;
+ }
+
+ dynsymbol_names = &mem[dyn_strtab->d_un.d_val - load->p_vaddr];
+ if (__ptr_oob(dynsymbol_names, mem, size))
+ goto err_oob;
+
+ hash = (void *)&mem[(unsigned long)dyn_hash->d_un.d_ptr - (unsigned long)load->p_vaddr];
+ if (__ptr_oob(hash, mem, size))
+ goto err_oob;
+
+ nbucket = hash[0];
+ nchain = hash[1];
+ bucket = &hash[2];
+ chain = &hash[nbucket + 2];
+
+ pr_debug("nbucket %lu nchain %lu bucket %p chain %p\n",
+ (long)nbucket, (long)nchain, bucket, chain);
+
+ for (i = 0; i < ARRAY_SIZE(vdso_symbols); i++) {
+ k = elf_hash((const unsigned char *)vdso_symbols[i]);
+
+ for (j = bucket[k % nbucket]; j < nchain && chain[j] != STN_UNDEF; j = chain[j]) {
+ Elf64_Sym *sym = (void *)&mem[dyn_symtab->d_un.d_ptr - load->p_vaddr];
+ char *name;
+
+ sym = &sym[j];
+ if (__ptr_oob(sym, mem, size))
+ continue;
+
+ if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC &&
+ ELF64_ST_BIND(sym->st_info) != STB_GLOBAL)
+ continue;
+
+ name = &dynsymbol_names[sym->st_name];
+ if (__ptr_oob(name, mem, size))
+ continue;
+
+ if (builtin_strcmp(name, vdso_symbols[i]))
+ continue;
+
+ builtin_memcpy(t->symbols[i].name, name, sizeof(t->symbols[i].name));
+ t->symbols[i].offset = (unsigned long)sym->st_value - load->p_vaddr;
+ break;
+ }
+ }
+
+ return 0;
+
+err_oob:
+ pr_err("Corrupted Elf data\n");
+ return -EFAULT;
+}
+
+static int vdso_remap(char *who, unsigned long from, unsigned long to, size_t size)
+{
+ unsigned long addr;
+
+ pr_debug("Remap %s %lx -> %lx\n", who, from, to);
+
+ addr = sys_mremap(from, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, to);
+ if (addr != to) {
+ pr_err("Unable to remap %lx -> %lx %lx\n",
+ from, to, addr);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Park runtime vDSO in some safe place where it can be accessible from restorer */
+int vdso_do_park(struct vdso_symtable *sym_rt, unsigned long park_at, unsigned long park_size)
+{
+ int ret;
+
+ BUG_ON((vdso_vma_size(sym_rt) + vvar_vma_size(sym_rt)) < park_size);
+
+ if (sym_rt->vvar_start != VDSO_BAD_ADDR) {
+ if (sym_rt->vma_start < sym_rt->vvar_start) {
+ ret = vdso_remap("rt-vdso", sym_rt->vma_start,
+ park_at, vdso_vma_size(sym_rt));
+ park_at += vdso_vma_size(sym_rt);
+ ret |= vdso_remap("rt-vvar", sym_rt->vvar_start,
+ park_at, vvar_vma_size(sym_rt));
+ } else {
+ ret = vdso_remap("rt-vvar", sym_rt->vvar_start,
+ park_at, vvar_vma_size(sym_rt));
+ park_at += vvar_vma_size(sym_rt);
+ ret |= vdso_remap("rt-vdso", sym_rt->vma_start,
+ park_at, vdso_vma_size(sym_rt));
+ }
+ } else
+ ret = vdso_remap("rt-vdso", sym_rt->vma_start,
+ park_at, vdso_vma_size(sym_rt));
+ return ret;
+}
+
+int vdso_proxify(char *who, struct vdso_symtable *sym_rt,
+ unsigned long vdso_rt_parked_at, size_t index,
+ VmaEntry *vmas, size_t nr_vmas)
+{
+ VmaEntry *vma_vdso = NULL, *vma_vvar = NULL;
+ struct vdso_symtable s = VDSO_SYMTABLE_INIT;
+ bool remap_rt = false;
+
+ /*
+ * Figue out which kind of vdso tuple we get.
+ */
+ if (vma_entry_is(&vmas[index], VMA_AREA_VDSO))
+ vma_vdso = &vmas[index];
+ else if (vma_entry_is(&vmas[index], VMA_AREA_VVAR))
+ vma_vvar = &vmas[index];
+
+ if (index < (nr_vmas - 1)) {
+ if (vma_entry_is(&vmas[index + 1], VMA_AREA_VDSO))
+ vma_vdso = &vmas[index + 1];
+ else if (vma_entry_is(&vmas[index + 1], VMA_AREA_VVAR))
+ vma_vvar = &vmas[index + 1];
+ }
+
+ if (!vma_vdso) {
+ pr_err("Can't find vDSO area in image\n");
+ return -1;
+ }
+
+ /*
+ * vDSO mark overwrites Elf program header of proxy vDSO thus
+ * it must never ever be greater in size.
+ */
+ BUILD_BUG_ON(sizeof(struct vdso_mark) > sizeof(Elf64_Phdr));
+
+ /*
+ * Find symbols in vDSO zone read from image.
+ */
+ if (vdso_fill_symtable((void *)vma_vdso->start, vma_entry_len(vma_vdso), &s))
+ return -1;
+
+ /*
+ * Proxification strategy
+ *
+ * - There might be two vDSO zones: vdso code and optionally vvar data
+ * - To be able to use in-place remapping we need
+ *
+ * a) Size and order of vDSO zones are to match
+ * b) Symbols offsets must match
+ * c) Have same number of vDSO zones
+ */
+ if (vma_entry_len(vma_vdso) == vdso_vma_size(sym_rt)) {
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(s.symbols); i++) {
+ if (s.symbols[i].offset != sym_rt->symbols[i].offset)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(s.symbols)) {
+ if (vma_vvar && sym_rt->vvar_start != VVAR_BAD_ADDR) {
+ remap_rt = (vvar_vma_size(sym_rt) == vma_entry_len(vma_vvar));
+ if (remap_rt) {
+ long delta_rt = sym_rt->vvar_start - sym_rt->vma_start;
+ long delta_this = vma_vvar->start - vma_vdso->start;
+
+ remap_rt = (delta_rt ^ delta_this) < 0 ? false : true;
+ }
+ } else
+ remap_rt = true;
+ }
+ }
+
+ pr_debug("image [vdso] %lx-%lx [vvar] %lx-%lx\n",
+ vma_vdso->start, vma_vdso->end,
+ vma_vvar ? vma_vvar->start : VVAR_BAD_ADDR,
+ vma_vvar ? vma_vvar->end : VVAR_BAD_ADDR);
+
+ /*
+ * Easy case -- the vdso from image has same offsets, order and size
+ * as runtime, so we simply remap runtime vdso to dumpee position
+ * without generating any proxy.
+ *
+ * Note we may remap VVAR vdso as well which might not yet been mapped
+ * by a caller code. So drop VMA_AREA_REGULAR from it and caller would
+ * not touch it anymore.
+ */
+ if (remap_rt) {
+ int ret = 0;
+
+ pr_info("Runtime vdso/vvar matches dumpee, remap inplace\n");
+
+ if (sys_munmap((void *)vma_vdso->start, vma_entry_len(vma_vdso))) {
+ pr_err("Failed to unmap %s\n", who);
+ return -1;
+ }
+
+ if (vma_vvar) {
+ if (sys_munmap((void *)vma_vvar->start, vma_entry_len(vma_vvar))) {
+ pr_err("Failed to unmap %s\n", who);
+ return -1;
+ }
+
+ if (vma_vdso->start < vma_vvar->start) {
+ ret = vdso_remap(who, vdso_rt_parked_at, vma_vdso->start, vdso_vma_size(sym_rt));
+ vdso_rt_parked_at += vdso_vma_size(sym_rt);
+ ret |= vdso_remap(who, vdso_rt_parked_at, vma_vvar->start, vvar_vma_size(sym_rt));
+ } else {
+ ret = vdso_remap(who, vdso_rt_parked_at, vma_vvar->start, vvar_vma_size(sym_rt));
+ vdso_rt_parked_at += vvar_vma_size(sym_rt);
+ ret |= vdso_remap(who, vdso_rt_parked_at, vma_vdso->start, vdso_vma_size(sym_rt));
+ }
+ } else
+ ret = vdso_remap(who, vdso_rt_parked_at, vma_vdso->start, vdso_vma_size(sym_rt));
+
+ return ret;
+ }
+
+ /*
+ * Now complex case -- we need to proxify calls. We redirect
+ * calls from dumpee vdso to runtime vdso, making dumpee
+ * to operate as proxy vdso.
+ */
+ pr_info("Runtime vdso mismatches dumpee, generate proxy\n");
+
+ /*
+ * Don't forget to shift if vvar is before vdso.
+ */
+ if (sym_rt->vvar_start != VDSO_BAD_ADDR &&
+ sym_rt->vvar_start < sym_rt->vma_start)
+ vdso_rt_parked_at += vvar_vma_size(sym_rt);
+
+ if (vdso_redirect_calls((void *)vdso_rt_parked_at,
+ (void *)vma_vdso->start,
+ sym_rt, &s)) {
+ pr_err("Failed to proxify dumpee contents\n");
+ return -1;
+ }
+
+ /*
+ * Put a special mark into runtime vdso, thus at next checkpoint
+ * routine we could detect this vdso and do not dump it, since
+ * it's auto-generated every new session if proxy required.
+ */
+ sys_mprotect((void *)vdso_rt_parked_at, vdso_vma_size(sym_rt), PROT_WRITE);
+ vdso_put_mark((void *)vdso_rt_parked_at, vma_vdso->start, vma_vvar ? vma_vvar->start : VVAR_BAD_ADDR);
+ sys_mprotect((void *)vdso_rt_parked_at, vdso_vma_size(sym_rt), VDSO_PROT);
+ return 0;
+}
diff --git a/arch/x86/vdso.c b/arch/x86/vdso.c
new file mode 100644
index 000000000..c692cd628
--- /dev/null
+++ b/arch/x86/vdso.c
@@ -0,0 +1,303 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "asm/types.h"
+#include "asm/parasite-syscall.h"
+
+#include "parasite-syscall.h"
+#include "parasite.h"
+#include "compiler.h"
+#include "kerndat.h"
+#include "vdso.h"
+#include "util.h"
+#include "log.h"
+#include "mem.h"
+#include "vma.h"
+
+#ifdef LOG_PREFIX
+# undef LOG_PREFIX
+#endif
+#define LOG_PREFIX "vdso: "
+
+struct vdso_symtable vdso_sym_rt = VDSO_SYMTABLE_INIT;
+u64 vdso_pfn = VDSO_BAD_PFN;
+/*
+ * The VMAs list might have proxy vdso/vvar areas left
+ * from previous dump/restore cycle so we need to detect
+ * them and eliminated from the VMAs list, they will be
+ * generated again on restore if needed.
+ */
+int parasite_fixup_vdso(struct parasite_ctl *ctl, pid_t pid,
+ struct vm_area_list *vma_area_list)
+{
+ unsigned long proxy_vdso_addr = VDSO_BAD_ADDR;
+ unsigned long proxy_vvar_addr = VVAR_BAD_ADDR;
+ struct vma_area *proxy_vdso_marked = NULL;
+ struct vma_area *proxy_vvar_marked = NULL;
+ struct parasite_vdso_vma_entry *args;
+ struct vma_area *vma;
+ int fd, ret = -1;
+ off_t off;
+ u64 pfn;
+
+ args = parasite_args(ctl, struct parasite_vdso_vma_entry);
+ fd = open_proc(pid, "pagemap");
+ if (fd < 0)
+ return -1;
+
+ list_for_each_entry(vma, &vma_area_list->h, list) {
+ if (!vma_area_is(vma, VMA_AREA_REGULAR))
+ continue;
+
+ if (vma_area_is(vma, VMA_FILE_SHARED) ||
+ vma_area_is(vma, VMA_FILE_PRIVATE))
+ continue;
+ /*
+ * It might be possible VVAR area from marked
+ * vDSO zone, we need to detect it earlier than
+ * VDSO_PROT test because VVAR_PROT is a subset
+ * of it but don't yield continue here,
+ * sigh... what a mess.
+ */
+ BUILD_BUG_ON(!(VDSO_PROT & VVAR_PROT));
+
+ if ((vma->e->prot & VVAR_PROT) == VVAR_PROT) {
+ if (proxy_vvar_addr != VVAR_BAD_ADDR &&
+ proxy_vvar_addr == vma->e->start) {
+ BUG_ON(proxy_vvar_marked);
+ proxy_vvar_marked = vma;
+ continue;
+ }
+ }
+
+ if ((vma->e->prot & VDSO_PROT) != VDSO_PROT)
+ continue;
+
+ if (vma->e->start > TASK_SIZE)
+ continue;
+
+ if (vma->e->flags & MAP_GROWSDOWN)
+ continue;
+
+ /*
+ * I need to poke every potentially marked vma,
+ * otherwise if task never called for vdso functions
+ * page frame number won't be reported.
+ */
+ args->start = vma->e->start;
+ args->len = vma_area_len(vma);
+
+ if (parasite_execute_daemon(PARASITE_CMD_CHECK_VDSO_MARK, ctl)) {
+ pr_err("vdso: Parasite failed to poke for mark\n");
+ ret = -1;
+ goto err;
+ }
+
+ /*
+ * Defer handling marked vdso until we walked over
+ * all vmas and restore potentially remapped vDSO
+ * area status.
+ */
+ if (unlikely(args->is_marked)) {
+ if (proxy_vdso_marked) {
+ pr_err("Ow! Second vdso mark detected!\n");
+ ret = -1;
+ goto err;
+ }
+ proxy_vdso_marked = vma;
+ proxy_vdso_addr = args->proxy_vdso_addr;
+ proxy_vvar_addr = args->proxy_vvar_addr;
+ continue;
+ }
+
+ off = (vma->e->start / PAGE_SIZE) * sizeof(u64);
+ ret = pread(fd, &pfn, sizeof(pfn), off);
+ if (ret < 0 || ret != sizeof(pfn)) {
+ pr_perror("Can't read pme for pid %d", pid);
+ ret = -1;
+ goto err;
+ }
+
+ pfn = PME_PFRAME(pfn);
+ if (!pfn) {
+ pr_err("Unexpected page fram number 0 for pid %d\n", pid);
+ ret = -1;
+ goto err;
+ }
+
+ /*
+ * Setup proper VMA status. Note starting with 3.16
+ * the [vdso]/[vvar] marks are reported correctly
+ * even when they are remapped into a new place,
+ * but only since that particular version of the
+ * kernel!
+ */
+ if (pfn == vdso_pfn) {
+ if (!vma_area_is(vma, VMA_AREA_VDSO)) {
+ pr_debug("vdso: Restore vDSO status by pfn at %lx\n",
+ (long)vma->e->start);
+ vma->e->status |= VMA_AREA_VDSO;
+ }
+ } else {
+ if (unlikely(vma_area_is(vma, VMA_AREA_VDSO))) {
+ pr_debug("vdso: Drop mishinted vDSO status at %lx\n",
+ (long)vma->e->start);
+ vma->e->status &= ~VMA_AREA_VDSO;
+ }
+ }
+ }
+
+ /*
+ * There is marked vdso, it means such vdso is autogenerated
+ * and must be dropped from vma list.
+ */
+ if (proxy_vdso_marked) {
+ pr_debug("vdso: Found marked at %lx (proxy vDSO at %lx VVAR at %lx)\n",
+ (long)proxy_vdso_marked->e->start,
+ (long)proxy_vdso_addr, (long)proxy_vvar_addr);
+
+ /*
+ * Don't forget to restore the proxy vdso/vvar status, since
+ * it's unknown to the kernel.
+ */
+ list_for_each_entry(vma, &vma_area_list->h, list) {
+ if (vma->e->start == proxy_vdso_addr) {
+ vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VDSO;
+ pr_debug("vdso: Restore proxy vDSO status at %lx\n",
+ (long)vma->e->start);
+ } else if (vma->e->start == proxy_vvar_addr) {
+ vma->e->status |= VMA_AREA_REGULAR | VMA_AREA_VVAR;
+ pr_debug("vdso: Restore proxy VVAR status at %lx\n",
+ (long)vma->e->start);
+ }
+ }
+
+ pr_debug("vdso: Droppping marked vdso at %lx\n",
+ (long)proxy_vdso_marked->e->start);
+ list_del(&proxy_vdso_marked->list);
+ xfree(proxy_vdso_marked);
+ vma_area_list->nr--;
+
+ if (proxy_vvar_marked) {
+ pr_debug("vdso: Droppping marked vvar at %lx\n",
+ (long)proxy_vvar_marked->e->start);
+ list_del(&proxy_vvar_marked->list);
+ xfree(proxy_vvar_marked);
+ vma_area_list->nr--;
+ }
+ }
+ ret = 0;
+err:
+ close(fd);
+ return ret;
+}
+
+static int vdso_fill_self_symtable(struct vdso_symtable *s)
+{
+ char buf[512];
+ int ret = -1;
+ FILE *maps;
+
+ *s = (struct vdso_symtable)VDSO_SYMTABLE_INIT;
+
+ maps = fopen_proc(PROC_SELF, "maps");
+ if (!maps) {
+ pr_perror("Can't open self-vma");
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), maps)) {
+ unsigned long start, end;
+ char *has_vdso, *has_vvar;
+
+ has_vdso = strstr(buf, "[vdso]");
+ if (!has_vdso)
+ has_vvar = strstr(buf, "[vvar]");
+ else
+ has_vvar = NULL;
+
+ if (!has_vdso && !has_vvar)
+ continue;
+
+ ret = sscanf(buf, "%lx-%lx", &start, &end);
+ if (ret != 2) {
+ ret = -1;
+ pr_err("Can't find vDSO/VVAR bounds\n");
+ goto err;
+ }
+
+ if (has_vdso) {
+ if (s->vma_start != VDSO_BAD_ADDR) {
+ pr_err("Got second vDSO entry\n");
+ ret = -1;
+ goto err;
+ }
+ s->vma_start = start;
+ s->vma_end = end;
+
+ ret = vdso_fill_symtable((void *)start, end - start, s);
+ if (ret)
+ goto err;
+ } else {
+ if (s->vvar_start != VVAR_BAD_ADDR) {
+ pr_err("Got second VVAR entry\n");
+ ret = -1;
+ goto err;
+ }
+ s->vvar_start = start;
+ s->vvar_end = end;
+ }
+ }
+
+ /*
+ * Validate its structure -- for new vDSO format the
+ * structure must be like
+ *
+ * 7fff1f5fd000-7fff1f5fe000 r-xp 00000000 00:00 0 [vdso]
+ * 7fff1f5fe000-7fff1f600000 r--p 00000000 00:00 0 [vvar]
+ *
+ * The areas may be in reverse order.
+ *
+ * 7fffc3502000-7fffc3504000 r--p 00000000 00:00 0 [vvar]
+ * 7fffc3504000-7fffc3506000 r-xp 00000000 00:00 0 [vdso]
+ *
+ */
+ ret = 0;
+ if (s->vma_start != VDSO_BAD_ADDR) {
+ if (s->vvar_start != VVAR_BAD_ADDR) {
+ if (s->vma_end != s->vvar_start &&
+ s->vvar_end != s->vma_start) {
+ ret = -1;
+ pr_err("Unexpected rt vDSO area bounds\n");
+ goto err;
+ }
+ }
+ } else {
+ ret = -1;
+ pr_err("Can't find rt vDSO\n");
+ goto err;
+ }
+
+ pr_debug("rt [vdso] %lx-%lx [vvar] %lx-%lx\n",
+ s->vma_start, s->vma_end,
+ s->vvar_start, s->vvar_end);
+
+err:
+ fclose(maps);
+ return ret;
+}
+
+int vdso_init(void)
+{
+ if (vdso_fill_self_symtable(&vdso_sym_rt))
+ return -1;
+ return vaddr_to_pfn(vdso_sym_rt.vma_start, &vdso_pfn);
+}
diff --git a/bfd.c b/bfd.c
new file mode 100644
index 000000000..9ebffc4a4
--- /dev/null
+++ b/bfd.c
@@ -0,0 +1,312 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "bug.h"
+#include "log.h"
+#include "bfd.h"
+#include "list.h"
+#include "util.h"
+#include "xmalloc.h"
+#include "asm-generic/page.h"
+
+#undef LOG_PREFIX
+#define LOG_PREFIX "bfd: "
+
+/*
+ * Kernel doesn't produce more than one page of
+ * date per one read call on proc files.
+ */
+#define BUFSIZE (PAGE_SIZE)
+
+struct bfd_buf {
+ char *mem;
+ struct list_head l;
+};
+
+static LIST_HEAD(bufs);
+
+#define BUFBATCH (16)
+
+static int buf_get(struct xbuf *xb)
+{
+ struct bfd_buf *b;
+
+ if (list_empty(&bufs)) {
+ void *mem;
+ int i;
+
+ mem = mmap(NULL, BUFBATCH * BUFSIZE, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, 0, 0);
+ if (mem == MAP_FAILED) {
+ pr_perror("No buf");
+ return -1;
+ }
+
+ for (i = 0; i < BUFBATCH; i++) {
+ b = xmalloc(sizeof(*b));
+ if (!b) {
+ if (i == 0) {
+ pr_err("No buffer for bfd\n");
+ return -1;
+ }
+
+ pr_warn("BFD buffers partial refil!\n");
+ break;
+ }
+
+ b->mem = mem + i * BUFSIZE;
+ list_add_tail(&b->l, &bufs);
+ }
+ }
+
+ b = list_first_entry(&bufs, struct bfd_buf, l);
+ list_del_init(&b->l);
+
+ xb->mem = b->mem;
+ xb->data = xb->mem;
+ xb->sz = 0;
+ xb->buf = b;
+ return 0;
+}
+
+static void buf_put(struct xbuf *xb)
+{
+ /*
+ * Don't unmap buffer back, it will get reused
+ * by next bfdopen call
+ */
+ list_add(&xb->buf->l, &bufs);
+ xb->buf = NULL;
+ xb->mem = NULL;
+ xb->data = NULL;
+}
+
+int bfdopen(struct bfd *f, int mode)
+{
+ if (buf_get(&f->b)) {
+ close(f->fd);
+ return -1;
+ }
+
+ f->mode = mode;
+ return 0;
+}
+
+static int bflush(struct bfd *bfd);
+static bool flush_failed = false;
+
+int bfd_flush_images(void)
+{
+ return flush_failed ? -1 : 0;
+}
+
+void bclose(struct bfd *f)
+{
+ if (bfd_buffered(f)) {
+ if ((f->mode != O_RDONLY) && bflush(f) < 0) {
+ /*
+ * This is to propagate error up. It's
+ * hardly possible by returning and
+ * checking it, but setting a static
+ * flag, failing further bfdopen-s and
+ * checking one at the end would work.
+ */
+ flush_failed = true;
+ pr_perror("Error flushing image");
+ }
+
+ buf_put(&f->b);
+ }
+ close_safe(&f->fd);
+}
+
+static int brefill(struct bfd *f)
+{
+ int ret;
+ struct xbuf *b = &f->b;
+
+ memmove(b->mem, b->data, b->sz);
+ b->data = b->mem;
+
+ ret = read(f->fd, b->mem + b->sz, BUFSIZE - b->sz);
+ if (ret < 0) {
+ pr_perror("Error reading file");
+ return -1;
+ }
+
+ if (ret == 0)
+ return 0;
+
+ b->sz += ret;
+ return 1;
+}
+
+static char *strnchr(char *str, unsigned int len, char c)
+{
+ while (len > 0 && *str != c) {
+ str++;
+ len--;
+ }
+
+ return len == 0 ? NULL : str;
+}
+
+char *breadline(struct bfd *f)
+{
+ struct xbuf *b = &f->b;
+ bool refilled = false;
+ char *n;
+ unsigned int ss = 0;
+
+again:
+ n = strnchr(b->data + ss, b->sz - ss, '\n');
+ if (n) {
+ char *ret;
+
+ ret = b->data;
+ b->data = n + 1; /* skip the \n found */
+ *n = '\0';
+ b->sz -= (b->data - ret);
+ return ret;
+ }
+
+ if (refilled) {
+ if (!b->sz)
+ return NULL;
+
+ /*
+ * Last bytes may lack the \n at the
+ * end, need to report this as full
+ * line anyway
+ */
+ b->data[b->sz] = '\0';
+
+ /*
+ * The b->data still points to old data,
+ * but we say that no bytes left there
+ * so next call to breadline will not
+ * "find" these bytes again.
+ */
+ b->sz = 0;
+ return b->data;
+ }
+
+ /*
+ * small optimization -- we've scanned b->sz
+ * symols already, no need to re-scan them after
+ * the buffer refill.
+ */
+ ss = b->sz;
+
+ /* no full line in the buffer -- refill one */
+ if (brefill(f) < 0)
+ return ERR_PTR(-EIO);
+
+ refilled = true;
+
+ goto again;
+}
+
+static int bflush(struct bfd *bfd)
+{
+ struct xbuf *b = &bfd->b;
+ int ret;
+
+ if (!b->sz)
+ return 0;
+
+ ret = write(bfd->fd, b->data, b->sz);
+ if (ret != b->sz)
+ return -1;
+
+ b->sz = 0;
+ return 0;
+}
+
+static int __bwrite(struct bfd *bfd, const void *buf, int size)
+{
+ struct xbuf *b = &bfd->b;
+
+ if (b->sz + size > BUFSIZE) {
+ int ret;
+ ret = bflush(bfd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (size > BUFSIZE)
+ return write(bfd->fd, buf, size);
+
+ memcpy(b->data + b->sz, buf, size);
+ b->sz += size;
+ return size;
+}
+
+int bwrite(struct bfd *bfd, const void *buf, int size)
+{
+ if (!bfd_buffered(bfd))
+ return write(bfd->fd, buf, size);
+
+ return __bwrite(bfd, buf, size);
+}
+
+int bwritev(struct bfd *bfd, const struct iovec *iov, int cnt)
+{
+ int i, written = 0;
+
+ if (!bfd_buffered(bfd))
+ return writev(bfd->fd, iov, cnt);
+
+ for (i = 0; i < cnt; i++) {
+ int ret;
+
+ ret = __bwrite(bfd, (const void *)iov[i].iov_base, iov[i].iov_len);
+ if (ret < 0)
+ return ret;
+
+ written += ret;
+ if (ret < iov[i].iov_len)
+ break;
+ }
+
+ return written;
+}
+
+int bread(struct bfd *bfd, void *buf, int size)
+{
+ struct xbuf *b = &bfd->b;
+ int more = 1, filled = 0;
+
+ if (!bfd_buffered(bfd))
+ return read(bfd->fd, buf, size);
+
+ while (more > 0) {
+ int chunk;
+
+ chunk = size - filled;
+ if (chunk > b->sz)
+ chunk = b->sz;
+
+ if (chunk) {
+ memcpy(buf + filled, b->data, chunk);
+ b->data += chunk;
+ b->sz -= chunk;
+ filled += chunk;
+ }
+
+ if (filled < size)
+ more = brefill(bfd);
+ else {
+ BUG_ON(filled > size);
+ more = 0;
+ }
+ }
+
+ return more < 0 ? more : filled;
+}
diff --git a/cgroup.c b/cgroup.c
new file mode 100644
index 000000000..f5da3edfa
--- /dev/null
+++ b/cgroup.c
@@ -0,0 +1,1345 @@
+#define LOG_PREFIX "cg: "
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "list.h"
+#include "xmalloc.h"
+#include "cgroup.h"
+#include "cr_options.h"
+#include "pstree.h"
+#include "proc_parse.h"
+#include "util.h"
+#include "imgset.h"
+#include "util-pie.h"
+#include "protobuf.h"
+#include "protobuf/core.pb-c.h"
+#include "protobuf/cgroup.pb-c.h"
+
+/*
+ * These string arrays have the names of all the properties that will be
+ * restored. To add a property for a cgroup type, add it to the
+ * corresponding char array above the NULL terminator. If you are adding
+ * a new cgroup family all together, you must also edit get_known_properties()
+ * Currently the code only supports properties with 1 value
+ */
+
+static const char *cpu_props[] = {
+ "cpu.shares",
+ "cpu.cfs_period_us",
+ "cpu.cfs_quota_us",
+ "cpu.rt_period_us",
+ "cpu.rt_runtime_us",
+ "notify_on_release",
+ NULL
+};
+
+static const char *memory_props[] = {
+ /* limit_in_bytes and memsw.limit_in_bytes must be set in this order */
+ "memory.limit_in_bytes",
+ "memory.memsw.limit_in_bytes",
+ "memory.use_hierarchy",
+ "notify_on_release",
+ NULL
+};
+
+static const char *cpuset_props[] = {
+ /*
+ * cpuset.cpus and cpuset.mems must be set before the process moves
+ * into its cgroup; they are "initialized" below to whatever the root
+ * values are in copy_special_cg_props so as not to cause ENOSPC when
+ * values are restored via this code.
+ */
+ "cpuset.cpus",
+ "cpuset.mems",
+ "cpuset.memory_migrate",
+ "cpuset.cpu_exclusive",
+ "cpuset.mem_exclusive",
+ "cpuset.mem_hardwall",
+ "cpuset.memory_spread_page",
+ "cpuset.memory_spread_slab",
+ "cpuset.sched_load_balance",
+ "cpuset.sched_relax_domain_level",
+ "notify_on_release",
+ NULL
+};
+
+static const char *blkio_props[] = {
+ "blkio.weight",
+ "notify_on_release",
+ NULL
+};
+
+static const char *freezer_props[] = {
+ "notify_on_release",
+ NULL
+};
+
+/*
+ * This structure describes set of controller groups
+ * a task lives in. The cg_ctl entries are stored in
+ * the @ctls list sorted by the .name field and then
+ * by the .path field.
+ */
+
+struct cg_set {
+ u32 id;
+ struct list_head l;
+ unsigned int n_ctls;
+ struct list_head ctls;
+};
+
+static LIST_HEAD(cg_sets);
+static unsigned int n_sets;
+static CgSetEntry **rst_sets;
+static unsigned int n_controllers;
+static CgControllerEntry **controllers;
+static char *cg_yard;
+static struct cg_set *root_cgset; /* Set root item lives in */
+static struct cg_set *criu_cgset; /* Set criu process lives in */
+static u32 cg_set_ids = 1;
+
+static LIST_HEAD(cgroups);
+static unsigned int n_cgroups;
+
+static CgSetEntry *find_rst_set_by_id(u32 id)
+{
+ int i;
+
+ for (i = 0; i < n_sets; i++)
+ if (rst_sets[i]->id == id)
+ return rst_sets[i];
+
+ return NULL;
+}
+
+#define CGCMP_MATCH 1 /* check for exact match */
+#define CGCMP_ISSUB 2 /* check set is subset of ctls */
+
+static bool cg_set_compare(struct cg_set *set, struct list_head *ctls, int what)
+{
+ struct list_head *l1 = &set->ctls, *l2 = ctls;
+
+ while (1) {
+ struct cg_ctl *c1 = NULL, *c2 = NULL;
+
+ if (l1->next != &set->ctls)
+ c1 = list_first_entry(l1, struct cg_ctl, l);
+ if (l2->next != ctls)
+ c2 = list_first_entry(l2, struct cg_ctl, l);
+
+ if (!c1 || !c2) /* Nowhere to move next */
+ return !c1 && !c2; /* Both lists scanned -- match */
+
+ if (strcmp(c1->name, c2->name))
+ return false;
+
+ switch (what) {
+ case CGCMP_MATCH:
+ if (strcmp(c1->path, c2->path))
+ return false;
+
+ break;
+ case CGCMP_ISSUB:
+ if (!strstartswith(c1->path, c2->path))
+ return false;
+
+ break;
+ }
+
+ l1 = l1->next;
+ l2 = l2->next;
+ }
+}
+
+static struct cg_set *get_cg_set(struct list_head *ctls, unsigned int n_ctls)
+{
+ struct cg_set *cs;
+
+ list_for_each_entry(cs, &cg_sets, l)
+ if (cg_set_compare(cs, ctls, CGCMP_MATCH)) {
+ pr_debug(" `- Existing css %d found\n", cs->id);
+ put_ctls(ctls);
+ return cs;
+ }
+
+ pr_debug(" `- New css ID %d\n", cg_set_ids);
+ cs = xmalloc(sizeof(*cs));
+ if (cs) {
+ cs->id = cg_set_ids++;
+ INIT_LIST_HEAD(&cs->ctls);
+ list_splice(ctls, &cs->ctls);
+ cs->n_ctls = n_ctls;
+ list_add_tail(&cs->l, &cg_sets);
+ n_sets++;
+
+ if (!pr_quelled(LOG_DEBUG)) {
+ struct cg_ctl *ctl;
+
+ list_for_each_entry(ctl, &cs->ctls, l)
+ pr_debug(" `- [%s] -> [%s]\n", ctl->name, ctl->path);
+ }
+ }
+
+ return cs;
+}
+
+struct cg_controller *new_controller(const char *name, int heirarchy)
+{
+ struct cg_controller *nc = xmalloc(sizeof(*nc));
+ if (!nc)
+ return NULL;
+
+ nc->controllers = xmalloc(sizeof(char *));
+ if (!nc->controllers) {
+ xfree(nc);
+ return NULL;
+ }
+
+ nc->controllers[0] = xstrdup(name);
+ if (!nc->controllers[0]) {
+ xfree(nc->controllers);
+ xfree(nc);
+ return NULL;
+ }
+
+ nc->n_controllers = 1;
+ nc->heirarchy = heirarchy;
+
+ nc->n_heads = 0;
+ INIT_LIST_HEAD(&nc->heads);
+
+ return nc;
+}
+
+int parse_cg_info(void)
+{
+ if (parse_cgroups(&cgroups, &n_cgroups) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Check that co-mounted controllers from /proc/cgroups (e.g. cpu and cpuacct)
+ * are contained in a comma separated string (e.g. from /proc/self/cgroup or
+ * mount options). */
+static bool cgroup_contains(char **controllers, unsigned int n_controllers, char *name)
+{
+ unsigned int i;
+ bool all_match = true;
+ for (i = 0; i < n_controllers; i++) {
+ bool found = false;
+ const char *loc = name;
+ do {
+ loc = strstr(loc, controllers[i]);
+ if (loc) {
+ loc += strlen(controllers[i]);
+ switch (*loc) {
+ case '\0':
+ case ',':
+ found = true;
+ break;
+ }
+ }
+ } while (loc);
+ all_match &= found;
+ }
+
+ return all_match && n_controllers > 0;
+}
+
+/* This is for use in add_cgroup() as additional arguments for the ftw()
+ * callback */
+static struct cg_controller *current_controller;
+static unsigned int path_pref_len;
+
+#define EXACT_MATCH 0
+#define PARENT_MATCH 1
+#define NO_MATCH 2
+
+static int find_dir(const char *path, struct list_head *dirs, struct cgroup_dir **rdir)
+{
+ struct cgroup_dir *d;
+ list_for_each_entry(d, dirs, siblings) {
+ if (strcmp(d->path, path) == 0) {
+ *rdir = d;
+ return EXACT_MATCH;
+ }
+
+ if (strstartswith(path, d->path)) {
+ int ret = find_dir(path, &d->children, rdir);
+ if (ret == NO_MATCH) {
+ *rdir = d;
+ return PARENT_MATCH;
+ }
+ return ret;
+
+ }
+ }
+
+ return NO_MATCH;
+}
+
+/*
+ * Strips trailing '\n' from the string
+ */
+static inline char *strip(char *str)
+{
+ char *e;
+
+ e = strchr(str, '\0');
+ if (e != str && *(e - 1) == '\n')
+ *(e - 1) = '\0';
+
+ return str;
+}
+
+/*
+ * Currently this function only supports properties that have a string value
+ * under 1024 chars.
+ */
+static int read_cgroup_prop(struct cgroup_prop *property, const char *fullpath)
+{
+ char buf[1024];
+ int fd, ret;
+
+ fd = open(fullpath, O_RDONLY);
+ if (fd == -1) {
+ property->value = NULL;
+ pr_perror("Failed opening %s", fullpath);
+ return -1;
+ }
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ if (ret == -1) {
+ pr_err("Failed scanning %s\n", fullpath);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ buf[ret] = 0;
+
+ if (strtoll(buf, NULL, 10) == LLONG_MAX)
+ strcpy(buf, "-1");
+
+ property->value = xstrdup(strip(buf));
+ if (!property->value)
+ return -1;
+
+ return 0;
+}
+
+static struct cgroup_prop *create_cgroup_prop(const char *name)
+{
+ struct cgroup_prop *property;
+
+ property = xmalloc(sizeof(*property));
+ if (!property)
+ return NULL;
+
+ property->name = xstrdup(name);
+ if (!property->name) {
+ xfree(property);
+ return NULL;
+ }
+
+ property->value = NULL;
+ return property;
+}
+
+static void free_cgroup_prop(struct cgroup_prop *prop)
+{
+ xfree(prop->name);
+ xfree(prop->value);
+ xfree(prop);
+}
+
+static void free_all_cgroup_props(struct cgroup_dir *ncd)
+{
+ struct cgroup_prop *prop, *t;
+
+ list_for_each_entry_safe(prop, t, &ncd->properties, list) {
+ list_del(&prop->list);
+ free_cgroup_prop(prop);
+ }
+
+ INIT_LIST_HEAD(&ncd->properties);
+ ncd->n_properties = 0;
+}
+
+static const char **get_known_properties(char *controller)
+{
+ const char **prop_arr = NULL;
+
+ if (!strcmp(controller, "cpu"))
+ prop_arr = cpu_props;
+ else if (!strcmp(controller, "memory"))
+ prop_arr = memory_props;
+ else if (!strcmp(controller, "cpuset"))
+ prop_arr = cpuset_props;
+ else if (!strcmp(controller, "blkio"))
+ prop_arr = blkio_props;
+ else if (!strcmp(controller, "freezer"))
+ prop_arr = freezer_props;
+
+ return prop_arr;
+}
+
+static int add_cgroup_properties(const char *fpath, struct cgroup_dir *ncd,
+ struct cg_controller *controller)
+{
+ int i, j;
+ char buf[PATH_MAX];
+ struct cgroup_prop *prop;
+
+ for (i = 0; i < controller->n_controllers; ++i) {
+
+ const char **prop_arr = get_known_properties(controller->controllers[i]);
+
+ for (j = 0; prop_arr != NULL && prop_arr[j] != NULL; ++j) {
+ if (snprintf(buf, PATH_MAX, "%s/%s", fpath, prop_arr[j]) >= PATH_MAX) {
+ pr_err("snprintf output was truncated");
+ return -1;
+ }
+
+ if (access(buf, F_OK) < 0 && errno == ENOENT) {
+ pr_info("Couldn't open %s. This cgroup property may not exist on this kernel\n", buf);
+ continue;
+ }
+
+ prop = create_cgroup_prop(prop_arr[j]);
+ if (!prop) {
+ free_all_cgroup_props(ncd);
+ return -1;
+ }
+
+ if (read_cgroup_prop(prop, buf) < 0) {
+ free_cgroup_prop(prop);
+ free_all_cgroup_props(ncd);
+ return -1;
+ }
+
+ pr_info("Dumping value %s from %s/%s\n", prop->value, fpath, prop->name);
+ list_add_tail(&prop->list, &ncd->properties);
+ ncd->n_properties++;
+ }
+ }
+
+ return 0;
+}
+
+static int add_cgroup(const char *fpath, const struct stat *sb, int typeflag)
+{
+ struct cgroup_dir *ncd = NULL, *match;
+ int ret = 0;
+
+ if (typeflag == FTW_D) {
+ int mtype;
+
+ pr_info("adding cgroup %s\n", fpath);
+
+ ncd = xmalloc(sizeof(*ncd));
+ if (!ncd)
+ goto out;
+
+ /* chop off the first "/proc/self/fd/N" str */
+ if (fpath[path_pref_len] == '\0')
+ ncd->path = xstrdup("/");
+ else
+ ncd->path = xstrdup(fpath + path_pref_len);
+
+ if (!ncd->path)
+ goto out;
+
+ mtype = find_dir(ncd->path, ¤t_controller->heads, &match);
+
+ switch (mtype) {
+ /* ignore co-mounted cgroups */
+ case EXACT_MATCH:
+ goto out;
+ case PARENT_MATCH:
+ list_add_tail(&ncd->siblings, &match->children);
+ match->n_children++;
+ break;
+ case NO_MATCH:
+ list_add_tail(&ncd->siblings, ¤t_controller->heads);
+ current_controller->n_heads++;
+ break;
+ default:
+ BUG();
+ }
+
+ INIT_LIST_HEAD(&ncd->children);
+ ncd->n_children = 0;
+
+ INIT_LIST_HEAD(&ncd->properties);
+ ncd->n_properties = 0;
+ if (add_cgroup_properties(fpath, ncd, current_controller) < 0) {
+ ret = -1;
+ goto out;
+ }
+
+ return 0;
+ } else
+ return 0;
+
+out:
+ if (ncd)
+ xfree(ncd->path);
+ xfree(ncd);
+ return ret;
+}
+
+static int collect_cgroups(struct list_head *ctls)
+{
+ struct cg_ctl *cc;
+ int ret = 0;
+ int fd = -1;
+
+ list_for_each_entry(cc, ctls, l) {
+ char path[PATH_MAX], mopts[1024];
+ char *name, prefix[] = ".criu.cgmounts.XXXXXX";
+ struct cg_controller *cg;
+
+ current_controller = NULL;
+
+ /* We should get all the "real" (i.e. not name=systemd type)
+ * controller from parse_cgroups(), so find that controller if
+ * it exists. */
+ list_for_each_entry(cg, &cgroups, l) {
+ if (cgroup_contains(cg->controllers, cg->n_controllers, cc->name)) {
+ current_controller = cg;
+ break;
+ }
+ }
+
+ if (!current_controller) {
+ /* only allow "fake" controllers to be created this way */
+ if (!strstartswith(cc->name, "name=")) {
+ pr_err("controller %s not found\n", cc->name);
+ ret = -1;
+ goto out;
+ } else {
+ struct cg_controller *nc = new_controller(cc->name, -1);
+ list_add_tail(&nc->l, &cg->l);
+ n_cgroups++;
+ current_controller = nc;
+ }
+ }
+
+ if (!opts.manage_cgroups)
+ continue;
+
+ if (strstartswith(cc->name, "name=")) {
+ name = cc->name + 5;
+ snprintf(mopts, sizeof(mopts), "none,%s", cc->name);
+ } else {
+ name = cc->name;
+ snprintf(mopts, sizeof(mopts), "%s", name);
+ }
+
+ if (mkdtemp(prefix) == NULL) {
+ pr_perror("can't make dir for cg mounts");
+ return -1;
+ }
+
+ if (mount("none", prefix, "cgroup", 0, mopts) < 0) {
+ pr_perror("couldn't mount %s", mopts);
+ rmdir(prefix);
+ return -1;
+ }
+
+ fd = open_detach_mount(prefix);
+ if (fd < 0)
+ return -1;
+
+ path_pref_len = snprintf(path, PATH_MAX, "/proc/self/fd/%d", fd);
+ snprintf(path + path_pref_len, PATH_MAX - path_pref_len, "%s", cc->path);
+
+ ret = ftw(path, add_cgroup, 4);
+ if (ret < 0) {
+ pr_perror("failed walking %s for empty cgroups", path);
+ goto out;
+ }
+
+out:
+ close_safe(&fd);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int dump_task_cgroup(struct pstree_item *item, u32 *cg_id)
+{
+ int pid;
+ LIST_HEAD(ctls);
+ unsigned int n_ctls = 0;
+ struct cg_set *cs;
+
+ if (item)
+ pid = item->pid.real;
+ else
+ pid = getpid();
+
+ pr_info("Dumping cgroups for %d\n", pid);
+ if (parse_task_cgroup(pid, &ctls, &n_ctls))
+ return -1;
+
+ cs = get_cg_set(&ctls, n_ctls);
+ if (!cs)
+ return -1;
+
+ if (!item) {
+ BUG_ON(criu_cgset);
+ criu_cgset = cs;
+ pr_info("Set %d is criu one\n", cs->id);
+ } else if (item == root_item) {
+ BUG_ON(root_cgset);
+ root_cgset = cs;
+ pr_info("Set %d is root one\n", cs->id);
+
+ /*
+ * The on-stack ctls is moved into cs inside
+ * the get_cg_set routine.
+ */
+ if (cs != criu_cgset && collect_cgroups(&cs->ctls))
+ return -1;
+ }
+
+ *cg_id = cs->id;
+ return 0;
+}
+
+static int dump_cg_dir_props(struct list_head *props, size_t n_props,
+ CgroupPropEntry ***ents)
+{
+ struct cgroup_prop *prop_cur;
+ CgroupPropEntry *cpe;
+ void *m;
+ int i = 0;
+
+ m = xmalloc(n_props * (sizeof(CgroupPropEntry *) + sizeof(CgroupPropEntry)));
+ *ents = m;
+ if (!m)
+ return -1;
+
+ cpe = m + n_props * sizeof(CgroupPropEntry *);
+
+ list_for_each_entry(prop_cur, props, list) {
+ cgroup_prop_entry__init(cpe);
+ cpe->name = xstrdup(prop_cur->name);
+ cpe->value = xstrdup(prop_cur->value);
+ if (!cpe->name || !cpe->value)
+ goto error;
+ (*ents)[i++] = cpe++;
+ }
+
+ return 0;
+
+error:
+ while (i >= 0) {
+ xfree(cpe->name);
+ xfree(cpe->value);
+ --cpe;
+ --i;
+ }
+
+ xfree(*ents);
+ return -1;
+}
+
+static int dump_cg_dirs(struct list_head *dirs, size_t n_dirs, CgroupDirEntry ***ents, int poff)
+{
+ struct cgroup_dir *cur;
+ CgroupDirEntry *cde;
+ void *m;
+ int i = 0;
+
+ m = xmalloc(n_dirs * (sizeof(CgroupDirEntry *) + sizeof(CgroupDirEntry)));
+ *ents = m;
+ if (!m)
+ return -1;
+
+ cde = m + n_dirs * sizeof(CgroupDirEntry *);
+
+ list_for_each_entry(cur, dirs, siblings) {
+ cgroup_dir_entry__init(cde);
+ cde->dir_name = cur->path + poff;
+ if (poff != 1) /* parent isn't "/" */
+ cde->dir_name++; /* leading / */
+ cde->n_children = cur->n_children;
+ if (cur->n_children > 0)
+ if (dump_cg_dirs(&cur->children, cur->n_children, &cde->children, strlen(cur->path)) < 0) {
+ xfree(*ents);
+ return -1;
+ }
+
+ cde->n_properties = cur->n_properties;
+ if (cde->n_properties > 0) {
+ if (dump_cg_dir_props(&cur->properties,
+ cde->n_properties, &cde->properties) < 0) {
+ xfree(*ents);
+ return -1;
+ }
+ }
+
+ (*ents)[i++] = cde++;
+ }
+
+ return 0;
+}
+
+static int dump_controllers(CgroupEntry *cg)
+{
+ struct cg_controller *cur;
+ CgControllerEntry *ce;
+ void *m;
+ int i;
+
+ cg->n_controllers = n_cgroups;
+ m = xmalloc(n_cgroups * (sizeof(CgControllerEntry *) + sizeof(CgControllerEntry)));
+ cg->controllers = m;
+ ce = m + cg->n_controllers * sizeof(CgControllerEntry *);
+ if (!m)
+ return -1;
+
+ i = 0;
+ list_for_each_entry(cur, &cgroups, l) {
+ cg_controller_entry__init(ce);
+
+ ce->cnames = cur->controllers;
+ ce->n_cnames = cur->n_controllers;
+ ce->n_dirs = cur->n_heads;
+ if (ce->n_dirs > 0)
+ if (dump_cg_dirs(&cur->heads, cur->n_heads, &ce->dirs, 0) < 0) {
+ xfree(cg->controllers);
+ return -1;
+ }
+ cg->controllers[i++] = ce++;
+ }
+
+ return 0;
+}
+
+
+static int dump_sets(CgroupEntry *cg)
+{
+ struct cg_set *set;
+ struct cg_ctl *ctl;
+ int s, c;
+ void *m;
+ CgSetEntry *se;
+ CgMemberEntry *ce;
+
+ pr_info("Dumping %d sets\n", n_sets - 1);
+
+ cg->n_sets = n_sets - 1;
+ m = xmalloc(cg->n_sets * (sizeof(CgSetEntry *) + sizeof(CgSetEntry)));
+ cg->sets = m;
+ se = m + cg->n_sets * sizeof(CgSetEntry *);
+ if (!m)
+ return -1;
+
+ s = 0;
+ list_for_each_entry(set, &cg_sets, l) {
+ if (set == criu_cgset)
+ continue;
+
+ /*
+ * Check that all sets we've found that tasks live in are
+ * subsets of the one root task lives in
+ */
+
+ pr_info(" `- Dumping %d set (%d ctls)\n", set->id, set->n_ctls);
+ if (!cg_set_compare(set, &root_cgset->ctls, CGCMP_ISSUB)) {
+ pr_err("Set %d is not subset of %d\n",
+ set->id, root_cgset->id);
+ return -1;
+ }
+
+ /*
+ * Now encode them onto the image entry
+ */
+
+ cg_set_entry__init(se);
+ se->id = set->id;
+
+ se->n_ctls = set->n_ctls;
+ m = xmalloc(se->n_ctls * (sizeof(CgMemberEntry *) + sizeof(CgMemberEntry)));
+ se->ctls = m;
+ ce = m + se->n_ctls * sizeof(CgMemberEntry *);
+ if (!m)
+ return -1;
+
+ c = 0;
+ list_for_each_entry(ctl, &set->ctls, l) {
+ pr_info(" `- Dumping %s of %s\n", ctl->name, ctl->path);
+ cg_member_entry__init(ce);
+ ce->name = ctl->name;
+ ce->path = ctl->path;
+ se->ctls[c++] = ce++;
+ }
+
+ cg->sets[s++] = se++;
+ }
+
+ return 0;
+}
+
+int dump_cgroups(void)
+{
+ CgroupEntry cg = CGROUP_ENTRY__INIT;
+
+ BUG_ON(!criu_cgset || !root_cgset);
+
+ /*
+ * Check whether root task lives in its own set as compared
+ * to criu. If yes, we should not dump anything, but make
+ * sure no other sets exist. The latter case can be supported,
+ * but requires some trickery and is hardly needed at the
+ * moment.
+ */
+
+ if (root_cgset == criu_cgset) {
+ if (!list_is_singular(&cg_sets)) {
+ pr_err("Non supported sub-cgroups found\n");
+ return -1;
+ }
+
+ pr_info("All tasks in criu's cgroups. Nothing to dump.\n");
+ return 0;
+ }
+
+ if (dump_sets(&cg))
+ return -1;
+ if (dump_controllers(&cg))
+ return -1;
+
+ pr_info("Writing CG image\n");
+ return pb_write_one(img_from_set(glob_imgset, CR_FD_CGROUP), &cg, PB_CGROUP);
+}
+
+static int ctrl_dir_and_opt(CgControllerEntry *ctl, char *dir, int ds,
+ char *opt, int os)
+{
+ int i, doff = 0, ooff = 0;
+ bool none_opt = false;
+
+ for (i = 0; i < ctl->n_cnames; i++) {
+ char *n;
+
+ n = ctl->cnames[i];
+ if (strstartswith(n, "name=")) {
+ n += 5;
+ if (opt && !none_opt) {
+ ooff += snprintf(opt + ooff, os - ooff, "none,");
+ none_opt = true;
+ }
+ }
+
+ doff += snprintf(dir + doff, ds - doff, "%s,", n);
+ if (opt)
+ ooff += snprintf(opt + ooff, os - ooff, "%s,", ctl->cnames[i]);
+ }
+
+ /* Chop the trailing ','-s */
+ dir[--doff] = '\0';
+ if (opt)
+ opt[ooff - 1] = '\0';
+
+ return doff;
+}
+
+static const char *special_cpuset_props[] = {
+ "cpuset.cpus",
+ "cpuset.mems",
+ NULL,
+};
+
+static int move_in_cgroup(CgSetEntry *se)
+{
+ int cg, i;
+
+ pr_info("Move into %d\n", se->id);
+ cg = get_service_fd(CGROUP_YARD);
+ for (i = 0; i < se->n_ctls; i++) {
+ char aux[PATH_MAX];
+ int fd, err, j, aux_off;
+ CgMemberEntry *ce = se->ctls[i];
+ CgControllerEntry *ctrl = NULL;
+
+ for (j = 0; j < n_controllers; j++) {
+ CgControllerEntry *cur = controllers[j];
+ if (cgroup_contains(cur->cnames, cur->n_cnames, ce->name)) {
+ ctrl = cur;
+ break;
+ }
+ }
+
+ if (!ctrl) {
+ pr_err("No cg_controller_entry found for %s/%s\n", ce->name, ce->path);
+ return -1;
+ }
+
+ aux_off = ctrl_dir_and_opt(ctrl, aux, sizeof(aux), NULL, 0);
+
+ snprintf(aux + aux_off, sizeof(aux) - aux_off, "/%s/tasks", ce->path);
+ pr_debug(" `-> %s\n", aux);
+ err = fd = openat(cg, aux, O_WRONLY);
+ if (fd >= 0) {
+ /*
+ * Writing zero into this file moves current
+ * task w/o any permissions checks :)
+ */
+ err = write(fd, "0", 1);
+ close(fd);
+ }
+
+ if (err < 0) {
+ pr_perror("Can't move into %s (%d/%d)", aux, err, fd);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int prepare_task_cgroup(struct pstree_item *me)
+{
+ CgSetEntry *se;
+ u32 current_cgset;
+
+ if (!rsti(me)->cg_set)
+ return 0;
+
+ if (me->parent)
+ current_cgset = rsti(me->parent)->cg_set;
+ else
+ current_cgset = root_cg_set;
+
+ if (rsti(me)->cg_set == current_cgset) {
+ pr_info("Cgroups %d inherited from parent\n", current_cgset);
+ return 0;
+ }
+
+ se = find_rst_set_by_id(rsti(me)->cg_set);
+ if (!se) {
+ pr_err("No set %d found\n", rsti(me)->cg_set);
+ return -1;
+ }
+
+ return move_in_cgroup(se);
+}
+
+void fini_cgroup(void)
+{
+ if (!cg_yard)
+ return;
+
+ close_service_fd(CGROUP_YARD);
+ umount2(cg_yard, MNT_DETACH);
+ rmdir(cg_yard);
+ xfree(cg_yard);
+ cg_yard = NULL;
+}
+
+static int restore_cgroup_prop(const CgroupPropEntry * cg_prop_entry_p,
+ char *path, int off)
+{
+ FILE *f;
+ int cg;
+
+ if (!cg_prop_entry_p->value) {
+ pr_err("cg_prop_entry->value was empty when should have had a value");
+ return -1;
+ }
+
+ if (snprintf(path + off, PATH_MAX - off, "/%s", cg_prop_entry_p->name) >= PATH_MAX) {
+ pr_err("snprintf output was truncated for %s\n", cg_prop_entry_p->name);
+ return -1;
+ }
+
+ cg = get_service_fd(CGROUP_YARD);
+ f = fopenat(cg, path, "w+");
+ if (!f) {
+ pr_perror("Failed opening %s for writing", path);
+ return -1;
+ }
+
+ if (fprintf(f, "%s", cg_prop_entry_p->value) < 0) {
+ fclose(f);
+ pr_err("Failed writing %s to %s\n", cg_prop_entry_p->value, path);
+ return -1;
+ }
+
+ if (fclose(f) != 0) {
+ pr_perror("Failed closing %s", path);
+ return -1;
+ }
+
+ pr_info("Restored cgroup property value %s to %s\n", cg_prop_entry_p->value, path);
+ return 0;
+}
+
+static int prepare_cgroup_dir_properties(char *path, int off, CgroupDirEntry **ents,
+ unsigned int n_ents)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < n_ents; i++) {
+ CgroupDirEntry *e = ents[i];
+ size_t off2 = off;
+
+ off2 += sprintf(path + off, "/%s", e->dir_name);
+ if (e->n_properties > 0) {
+ for (j = 0; j < e->n_properties; ++j) {
+ if (restore_cgroup_prop(e->properties[j], path, off2) < 0)
+ return -1;
+ }
+ }
+
+ if (prepare_cgroup_dir_properties(path, off2, e->children, e->n_children) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int prepare_cgroup_properties(void)
+{
+ char cname_path[PATH_MAX];
+ unsigned int i, off;
+
+ for (i = 0; i < n_controllers; i++) {
+ CgControllerEntry *c = controllers[i];
+
+ if (c->n_cnames < 1) {
+ pr_err("Each CgControllerEntry should have at least 1 cname\n");
+ return -1;
+ }
+
+ off = ctrl_dir_and_opt(c, cname_path, sizeof(cname_path), NULL, 0);
+ if (prepare_cgroup_dir_properties(cname_path, off, c->dirs, c->n_dirs) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int restore_special_cpuset_props(char *paux, size_t off, CgroupDirEntry *e)
+{
+ int i, j;
+
+ for (i = 0; special_cpuset_props[i]; i++) {
+ const char *name = special_cpuset_props[i];
+
+ for (j = 0; j < e->n_properties; j++) {
+ CgroupPropEntry *prop = e->properties[j];
+
+ if (strcmp(name, prop->name) == 0)
+ if (restore_cgroup_prop(prop, paux, off) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int prepare_cgroup_dirs(char **controllers, int n_controllers, char *paux, size_t off,
+ CgroupDirEntry **ents, size_t n_ents)
+{
+ size_t i, j;
+ CgroupDirEntry *e;
+ int cg = get_service_fd(CGROUP_YARD);
+
+ for (i = 0; i < n_ents; i++) {
+ size_t off2 = off;
+ e = ents[i];
+ struct stat st;
+
+ off2 += sprintf(paux + off, "/%s", e->dir_name);
+
+ /*
+ * Checking to see if file already exists. If not, create it. If
+ * it does exist, prevent us from overwriting the properties
+ * later by removing the CgroupDirEntry's properties.
+ */
+ if (fstatat(cg, paux, &st, 0) < 0) {
+ if (errno != ENOENT) {
+ pr_perror("Failed accessing file %s", paux);
+ return -1;
+ }
+
+ if (mkdirpat(cg, paux)) {
+ pr_perror("Can't make cgroup dir %s", paux);
+ return -1;
+ }
+ pr_info("Created dir %s\n", paux);
+
+ for (j = 0; j < n_controllers; j++) {
+ if (strcmp(controllers[j], "cpuset") == 0) {
+ if (restore_special_cpuset_props(paux, off2, e) < 0) {
+ pr_err("Restoring special cpuset props failed!\n");
+ return -1;
+ }
+ }
+ }
+ } else {
+ if (e->n_properties > 0) {
+ xfree(e->properties);
+ e->properties = NULL;
+ e->n_properties = 0;
+ }
+ pr_info("Determined dir %s already existed\n", paux);
+ }
+
+ if (prepare_cgroup_dirs(controllers, n_controllers, paux, off2,
+ e->children, e->n_children) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Prepare the CGROUP_YARD service descriptor. This guy is
+ * tmpfs mount with the set of ctl->name directories each
+ * one having the respective cgroup mounted.
+ *
+ * It's required for two reasons.
+ *
+ * First, if we move more than one task into cgroups it's
+ * faster to have cgroup tree visible by them all in sime
+ * single place. Searching for this thing existing in the
+ * criu's space is not nice, as parsing /proc/mounts is not
+ * very fast, other than this not all cgroups may be mounted.
+ *
+ * Second, when we have user-namespaces support we will
+ * loose the ability to mount cgroups on-demand, so prepare
+ * them in advance.
+ */
+
+static int prepare_cgroup_sfd(CgroupEntry *ce)
+{
+ int off, i, ret;
+ char paux[PATH_MAX];
+
+ pr_info("Preparing cgroups yard\n");
+
+ off = sprintf(paux, ".criu.cgyard.XXXXXX");
+ if (mkdtemp(paux) == NULL) {
+ pr_perror("Can't make temp cgyard dir");
+ return -1;
+ }
+
+ cg_yard = xstrdup(paux);
+ if (!cg_yard) {
+ rmdir(paux);
+ return -1;
+ }
+
+ if (mount("none", cg_yard, "tmpfs", 0, NULL)) {
+ pr_perror("Can't mount tmpfs in cgyard");
+ goto err;
+ }
+
+ if (mount("none", cg_yard, NULL, MS_PRIVATE, NULL)) {
+ pr_perror("Can't make cgyard private");
+ goto err;
+ }
+
+ pr_debug("Opening %s as cg yard\n", cg_yard);
+ i = open(cg_yard, O_DIRECTORY);
+ if (i < 0) {
+ pr_perror("Can't open cgyard");
+ goto err;
+ }
+
+ ret = install_service_fd(CGROUP_YARD, i);
+ close(i);
+ if (ret < 0)
+ goto err;
+
+
+ paux[off++] = '/';
+
+ for (i = 0; i < ce->n_controllers; i++) {
+ int ctl_off = off, yard_off;
+ char opt[128], *yard;
+ CgControllerEntry *ctrl = ce->controllers[i];
+
+ if (ctrl->n_cnames < 1) {
+ pr_err("Each cg_controller_entry must have at least 1 controller");
+ goto err;
+ }
+
+ ctl_off += ctrl_dir_and_opt(ctrl,
+ paux + ctl_off, sizeof(paux) - ctl_off,
+ opt, sizeof(opt));
+
+ pr_debug("\tMaking subdir %s (%s)\n", paux, opt);
+ if (mkdir(paux, 0700)) {
+ pr_perror("Can't make cgyard subdir %s", paux);
+ goto err;
+ }
+
+ if (mount("none", paux, "cgroup", 0, opt) < 0) {
+ pr_perror("Can't mount %s cgyard", paux);
+ goto err;
+ }
+
+ /* We skip over the .criu.cgyard.XXXXXX/, since those will be
+ * referred to by the cg yard service fd. */
+ yard = paux + strlen(cg_yard) + 1;
+ yard_off = ctl_off - (strlen(cg_yard) + 1);
+ if (opts.manage_cgroups &&
+ prepare_cgroup_dirs(ctrl->cnames, ctrl->n_cnames, yard, yard_off,
+ ctrl->dirs, ctrl->n_dirs))
+ goto err;
+
+ }
+
+ return 0;
+
+err:
+ fini_cgroup();
+ return -1;
+}
+
+static int rewrite_cgsets(CgroupEntry *cge, char **controllers, int n_controllers,
+ char *from, char *to)
+{
+ int i, j;
+ for (i = 0; i < cge->n_sets; i++) {
+ CgSetEntry *set = cge->sets[i];
+ for (j = 0; j < set->n_ctls; j++) {
+ CgMemberEntry *cg = set->ctls[j];
+ if (cgroup_contains(controllers, n_controllers, cg->name) &&
+ /* +1 to get rid of leading / */
+ strstartswith(cg->path + 1, from)) {
+
+ /* +1 to get rid of leading /, again */
+ int off = strlen(from) + 1;
+
+ /* +1 for trailing NULL */
+ int newlen = strlen(to) + strlen(cg->path + off) + 1;
+ char *m = xmalloc(newlen * sizeof(char*));
+ if (!m)
+ return -1;
+
+ sprintf(m, "%s%s", to, cg->path + off);
+ free(cg->path);
+ cg->path = m;
+ }
+ }
+
+ }
+ return 0;
+}
+
+static int rewrite_cgroup_roots(CgroupEntry *cge)
+{
+ int i, j;
+ struct cg_root_opt *o;
+ char *newroot = NULL;
+
+ for (i = 0; i < cge->n_controllers; i++) {
+ CgControllerEntry *ctrl = cge->controllers[i];
+ newroot = opts.new_global_cg_root;
+
+ list_for_each_entry(o, &opts.new_cgroup_roots, node) {
+ if (cgroup_contains(ctrl->cnames, ctrl->n_cnames, o->controller)) {
+ newroot = o->newroot;
+ break;
+ }
+
+ }
+
+ if (newroot) {
+ for (j = 0; j < ctrl->n_dirs; j++) {
+ CgroupDirEntry *cgde = ctrl->dirs[j];
+ char *m;
+
+ pr_info("rewriting %s to %s\n", cgde->dir_name, newroot);
+ if (rewrite_cgsets(cge, ctrl->cnames, ctrl->n_cnames, cgde->dir_name, newroot))
+ return -1;
+
+ m = xstrdup(newroot);
+ if (!m)
+ return -1;
+
+ free(cgde->dir_name);
+ cgde->dir_name = m;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int prepare_cgroup(void)
+{
+ int ret;
+ struct cr_img *img;
+ CgroupEntry *ce;
+
+ img = open_image(CR_FD_CGROUP, O_RSTR | O_OPT);
+ if (!img) {
+ if (errno == ENOENT) /* backward compatibility */
+ return 0;
+ else
+ return -1;
+ }
+
+ ret = pb_read_one_eof(img, &ce, PB_CGROUP);
+ close_image(img);
+ if (ret <= 0) /* Zero is OK -- no sets there. */
+ return ret;
+
+ if (rewrite_cgroup_roots(ce))
+ return -1;
+
+ n_sets = ce->n_sets;
+ rst_sets = ce->sets;
+ n_controllers = ce->n_controllers;
+ controllers = ce->controllers;
+
+ if (n_sets)
+ /*
+ * We rely on the fact that all sets contain the same
+ * set of controllers. This is checked during dump
+ * with cg_set_compare(CGCMP_ISSUB) call.
+ */
+ ret = prepare_cgroup_sfd(ce);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+int new_cg_root_add(char *controller, char *newroot)
+{
+ struct cg_root_opt *o;
+
+ if (!controller) {
+ opts.new_global_cg_root = newroot;
+ return 0;
+ }
+
+ o = xmalloc(sizeof(*o));
+ if (!o)
+ return -1;
+
+ o->controller = controller;
+ o->newroot = newroot;
+ list_add(&o->node, &opts.new_cgroup_roots);
+ return 0;
+}
diff --git a/compel/.gitignore b/compel/.gitignore
deleted file mode 100644
index 5e770a86c..000000000
--- a/compel/.gitignore
+++ /dev/null
@@ -1,17 +0,0 @@
-arch/x86/plugins/std/sys-exec-tbl-64.c
-arch/x86/plugins/std/syscalls-64.S
-arch/arm/plugins/std/syscalls/syscalls.S
-arch/aarch64/plugins/std/syscalls/syscalls.S
-arch/s390/plugins/std/syscalls/syscalls.S
-arch/ppc64/plugins/std/syscalls/syscalls.S
-arch/mips/plugins/std/syscalls/syscalls-64.S
-arch/loongarch64/plugins/std/syscalls/syscalls-64.S
-arch/riscv64/plugins/std/syscalls/syscalls.S
-include/version.h
-plugins/include/uapi/std/asm/syscall-types.h
-plugins/include/uapi/std/syscall-64.h
-plugins/include/uapi/std/syscall-codes-64.h
-plugins/include/uapi/std/syscall-codes.h
-plugins/include/uapi/std/syscall.h
-plugins/include/uapi/std/syscall-aux.h
-plugins/include/uapi/std/syscall-aux.S
diff --git a/compel/Makefile b/compel/Makefile
deleted file mode 100644
index c0b8a82a0..000000000
--- a/compel/Makefile
+++ /dev/null
@@ -1,85 +0,0 @@
-include Makefile.versions
-
-COMPEL_SO_VERSION := $(COMPEL_SO_VERSION_MAJOR)$(if $(COMPEL_SO_VERSION_MINOR),.$(COMPEL_SO_VERSION_MINOR))$(if $(COMPEL_SO_VERSION_SUBLEVEL),.$(COMPEL_SO_VERSION_SUBLEVEL))
-COMPEL_SO_VERSION_CODE := $(shell expr $(COMPEL_SO_VERSION_MAJOR) \* 65536 \+ $(COMPEL_SO_VERSION_MINOR) \* 256 \+ $(COMPEL_SO_VERSION_SUBLEVEL))
-ccflags-y += -DINCLUDEDIR=\"$(INCLUDEDIR)\"
-ccflags-y += -DLIBEXECDIR=\"$(LIBEXECDIR)\"
-ccflags-y += -DLIBDIR=\"$(LIBDIR)\"
-ccflags-y += -DSTATIC_LIB=\"$(LIBCOMPEL_A)\"
-ccflags-y += -DDYN_LIB=\"$(LIBCOMPEL_SO).$(COMPEL_SO_VERSION_MAJOR)\"
-ccflags-y += -iquote compel/arch/$(ARCH)/src/lib/include
-ccflags-y += -iquote compel/include
-ccflags-y += -fno-strict-aliasing
-ccflags-y += -fPIC
-ldflags-y += -r
-
-#
-# UAPI inclusion, referred as
-ccflags-y += -I compel/include/uapi
-
-lib-name := $(LIBCOMPEL_A)
-lib-y += src/lib/log.o
-host-lib-y += src/lib/log.o
-
-lib-y += arch/$(ARCH)/src/lib/cpu.o
-lib-y += arch/$(ARCH)/src/lib/infect.o
-lib-y += src/lib/infect-rpc.o
-lib-y += src/lib/infect-util.o
-lib-y += src/lib/infect.o
-lib-y += src/lib/ptrace.o
-
-ifeq ($(ARCH),x86)
-lib-y += arch/$(ARCH)/src/lib/thread_area.o
-endif
-
-# handle_elf() has no support of ELF relocations on ARM and RISCV64 (yet?)
-ifneq ($(filter arm aarch64 loongarch64 riscv64,$(ARCH)),)
-CFLAGS += -DNO_RELOCS
-HOSTCFLAGS += -DNO_RELOCS
-endif
-
-obj-y += src/main.o
-obj-y += arch/$(ARCH)/src/lib/handle-elf.o
-obj-y += src/lib/handle-elf.o
-
-host-ccflags-y += $(ccflags-y)
-
-hostprogs-y += compel-host-bin
-compel-host-bin-objs := $(patsubst %.o,%-host.o,$(obj-y) $(host-lib-y))
-
-cleanup-y += compel/compel
-cleanup-y += compel/compel-host-bin
-cleanup-y += compel/libcompel.so
-
-install: compel/compel compel/$(LIBCOMPEL_SO) compel/$(LIBCOMPEL_A)
- $(E) " INSTALL " compel
- $(Q) mkdir -p $(DESTDIR)$(BINDIR)
- $(Q) install -m 755 compel/compel $(DESTDIR)$(BINDIR)
- $(E) " INSTALL " $(LIBCOMPEL_SO)
- $(Q) mkdir -p $(DESTDIR)$(LIBDIR)
- $(Q) install -m 0644 compel/$(LIBCOMPEL_SO) $(DESTDIR)$(LIBDIR)
- $(Q) install -m 755 compel/$(LIBCOMPEL_SO) $(DESTDIR)$(LIBDIR)/$(LIBCOMPEL_SO).$(COMPEL_SO_VERSION_MAJOR).$(COMPEL_SO_VERSION_MINOR)
- $(Q) ln -fns $(LIBCOMPEL_SO).$(COMPEL_SO_VERSION_MAJOR).$(COMPEL_SO_VERSION_MINOR) $(DESTDIR)$(LIBDIR)/$(LIBCOMPEL_SO).$(COMPEL_SO_VERSION_MAJOR)
- $(Q) ln -fns $(LIBCOMPEL_SO).$(COMPEL_SO_VERSION_MAJOR).$(COMPEL_SO_VERSION_MINOR) $(DESTDIR)$(LIBDIR)/$(LIBCOMPEL_SO)
- $(E) " INSTALL " $(LIBCOMPEL_A)
- $(Q) install -m 0644 compel/$(LIBCOMPEL_A) $(DESTDIR)$(LIBDIR)
- $(E) " INSTALL " compel uapi
- $(Q) mkdir -p $(DESTDIR)$(INCLUDEDIR)/compel/asm
- $(Q) cp compel/include/uapi/*.h $(DESTDIR)$(INCLUDEDIR)/compel/
- $(Q) cp compel/include/uapi/asm/*.h $(DESTDIR)$(INCLUDEDIR)/compel/asm/
- $(Q) mkdir -p $(DESTDIR)$(INCLUDEDIR)/compel/common/asm
- $(Q) cp include/common/compiler.h $(DESTDIR)$(INCLUDEDIR)/compel/common/
-.PHONY: install
-
-uninstall:
- $(E) " UNINSTALL" compel
- $(Q) $(RM) $(addprefix $(DESTDIR)$(BINDIR)/,compel)
- $(E) " UNINSTALL" $(LIBCOMPEL_SO)
- $(Q) $(RM) $(addprefix $(DESTDIR)$(LIBDIR)/,$(LIBCOMPEL_SO))
- $(Q) $(RM) $(addprefix $(DESTDIR)$(LIBDIR)/,$(LIBCOMPEL_SO).$(COMPEL_SO_VERSION_MAJOR))
- $(Q) $(RM) $(addprefix $(DESTDIR)$(LIBDIR)/,$(LIBCOMPEL_SO).$(COMPEL_SO_VERSION_MAJOR).$(COMPEL_SO_VERSION_MINOR))
- $(E) " UNINSTALL" $(LIBCOMPEL_A)
- $(Q) $(RM) $(addprefix $(DESTDIR)$(LIBDIR)/,$(LIBCOMPEL_A))
- $(E) " UNINSTALL" compel uapi
- $(Q) $(RM) -rf $(addprefix $(DESTDIR)$(INCLUDEDIR)/,compel/*)
-.PHONY: uninstall
diff --git a/compel/arch/aarch64/plugins/include/asm/prologue.h b/compel/arch/aarch64/plugins/include/asm/prologue.h
deleted file mode 120000
index e0275e350..000000000
--- a/compel/arch/aarch64/plugins/include/asm/prologue.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../arch/x86/plugins/include/asm/prologue.h
\ No newline at end of file
diff --git a/compel/arch/aarch64/plugins/include/asm/syscall-types.h b/compel/arch/aarch64/plugins/include/asm/syscall-types.h
deleted file mode 100644
index 45fd57af6..000000000
--- a/compel/arch/aarch64/plugins/include/asm/syscall-types.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef COMPEL_ARCH_SYSCALL_TYPES_H__
-#define COMPEL_ARCH_SYSCALL_TYPES_H__
-
-#define SA_RESTORER 0x04000000
-
-typedef void rt_signalfn_t(int, siginfo_t *, void *);
-typedef rt_signalfn_t *rt_sighandler_t;
-
-typedef void rt_restorefn_t(void);
-typedef rt_restorefn_t *rt_sigrestore_t;
-
-#define _KNSIG 64
-#define _NSIG_BPW 64
-
-#define _KNSIG_WORDS (_KNSIG / _NSIG_BPW)
-
-typedef struct {
- unsigned long sig[_KNSIG_WORDS];
-} k_rtsigset_t;
-
-typedef struct {
- rt_sighandler_t rt_sa_handler;
- unsigned long rt_sa_flags;
- rt_sigrestore_t rt_sa_restorer;
- k_rtsigset_t rt_sa_mask;
-} rt_sigaction_t;
-
-#endif /* COMPEL_ARCH_SYSCALL_TYPES_H__ */
diff --git a/compel/arch/aarch64/plugins/include/features.h b/compel/arch/aarch64/plugins/include/features.h
deleted file mode 100644
index b4a3cded2..000000000
--- a/compel/arch/aarch64/plugins/include/features.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef __COMPEL_ARCH_FEATURES_H
-#define __COMPEL_ARCH_FEATURES_H
-
-#endif /* __COMPEL_ARCH_FEATURES_H */
diff --git a/compel/arch/aarch64/plugins/std/parasite-head.S b/compel/arch/aarch64/plugins/std/parasite-head.S
deleted file mode 100644
index 456c2117d..000000000
--- a/compel/arch/aarch64/plugins/std/parasite-head.S
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "common/asm/linkage.h"
-
- .section .head.text, "ax"
-ENTRY(__export_parasite_head_start)
- bl parasite_service
- brk #0 // the instruction BRK #0 generates the signal SIGTRAP in Linux
-END(__export_parasite_head_start)
diff --git a/compel/arch/aarch64/plugins/std/syscalls/Makefile.syscalls b/compel/arch/aarch64/plugins/std/syscalls/Makefile.syscalls
deleted file mode 120000
index eba4d986c..000000000
--- a/compel/arch/aarch64/plugins/std/syscalls/Makefile.syscalls
+++ /dev/null
@@ -1 +0,0 @@
-../../../../arm/plugins/std/syscalls/Makefile.syscalls
\ No newline at end of file
diff --git a/compel/arch/aarch64/plugins/std/syscalls/gen-sys-exec-tbl.pl b/compel/arch/aarch64/plugins/std/syscalls/gen-sys-exec-tbl.pl
deleted file mode 120000
index 8d7e897ae..000000000
--- a/compel/arch/aarch64/plugins/std/syscalls/gen-sys-exec-tbl.pl
+++ /dev/null
@@ -1 +0,0 @@
-../../../../arm/plugins/std/syscalls/gen-sys-exec-tbl.pl
\ No newline at end of file
diff --git a/compel/arch/aarch64/plugins/std/syscalls/gen-syscalls.pl b/compel/arch/aarch64/plugins/std/syscalls/gen-syscalls.pl
deleted file mode 120000
index 5c9563611..000000000
--- a/compel/arch/aarch64/plugins/std/syscalls/gen-syscalls.pl
+++ /dev/null
@@ -1 +0,0 @@
-../../../../arm/plugins/std/syscalls/gen-syscalls.pl
\ No newline at end of file
diff --git a/compel/arch/aarch64/plugins/std/syscalls/syscall-aux.h b/compel/arch/aarch64/plugins/std/syscalls/syscall-aux.h
deleted file mode 100644
index 3c7124856..000000000
--- a/compel/arch/aarch64/plugins/std/syscalls/syscall-aux.h
+++ /dev/null
@@ -1,3 +0,0 @@
-#ifndef __NR_openat
-#define __NR_openat 56
-#endif
diff --git a/compel/arch/aarch64/plugins/std/syscalls/syscall-common.S b/compel/arch/aarch64/plugins/std/syscalls/syscall-common.S
deleted file mode 100644
index aeb89ea88..000000000
--- a/compel/arch/aarch64/plugins/std/syscalls/syscall-common.S
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "common/asm/linkage.h"
-
-syscall_common:
- svc #0
- ret
-
-
-.macro syscall name, nr
- ENTRY(\name)
- mov x8, \nr
- b syscall_common
- END(\name)
-.endm
-
-
-ENTRY(__cr_restore_rt)
- mov x8, __NR_rt_sigreturn
- svc #0
-END(__cr_restore_rt)
diff --git a/compel/arch/aarch64/plugins/std/syscalls/syscall.def b/compel/arch/aarch64/plugins/std/syscalls/syscall.def
deleted file mode 120000
index ebecde3cb..000000000
--- a/compel/arch/aarch64/plugins/std/syscalls/syscall.def
+++ /dev/null
@@ -1 +0,0 @@
-../../../../arm/plugins/std/syscalls/syscall.def
\ No newline at end of file
diff --git a/compel/arch/aarch64/scripts/compel-pack.lds.S b/compel/arch/aarch64/scripts/compel-pack.lds.S
deleted file mode 100644
index 9dfb0f8ad..000000000
--- a/compel/arch/aarch64/scripts/compel-pack.lds.S
+++ /dev/null
@@ -1,36 +0,0 @@
-OUTPUT_ARCH(aarch64)
-EXTERN(__export_parasite_head_start)
-
-SECTIONS
-{
- .crblob 0x0 : {
- *(.head.text)
- ASSERT(DEFINED(__export_parasite_head_start),
- "Symbol __export_parasite_head_start is missing");
- *(.text*)
- . = ALIGN(32);
- *(.data*)
- . = ALIGN(32);
- *(.rodata*)
- . = ALIGN(32);
- *(.bss*)
- . = ALIGN(32);
- *(.got*)
- . = ALIGN(32);
- *(.toc*)
- . = ALIGN(32);
- } =0x00000000
-
- .shstrtab : { *(.shstrtab) }
- .strtab : { *(.strtab) }
- .symtab : { *(.symtab) }
-
- /DISCARD/ : {
- *(.debug*)
- *(.comment*)
- *(.note*)
- *(.group*)
- *(.eh_frame*)
- *(*)
- }
-}
diff --git a/compel/arch/aarch64/src/lib/cpu.c b/compel/arch/aarch64/src/lib/cpu.c
deleted file mode 100644
index 538a29887..000000000
--- a/compel/arch/aarch64/src/lib/cpu.c
+++ /dev/null
@@ -1,78 +0,0 @@
-#include
-#include
-
-#include "compel-cpu.h"
-
-#include "common/bitops.h"
-
-#include "log.h"
-
-#undef LOG_PREFIX
-#define LOG_PREFIX "cpu: "
-
-static compel_cpuinfo_t rt_info;
-
-static void fetch_rt_cpuinfo(void)
-{
- static bool rt_info_done = false;
-
- if (!rt_info_done) {
- compel_cpuid(&rt_info);
- rt_info_done = true;
- }
-}
-
-void compel_set_cpu_cap(compel_cpuinfo_t *info, unsigned int feature)
-{
-}
-void compel_clear_cpu_cap(compel_cpuinfo_t *info, unsigned int feature)
-{
-}
-int compel_test_cpu_cap(compel_cpuinfo_t *info, unsigned int feature)
-{
- return 0;
-}
-int compel_test_fpu_cap(compel_cpuinfo_t *info, unsigned int feature)
-{
- return 0;
-}
-int compel_cpuid(compel_cpuinfo_t *info)
-{
- return 0;
-}
-
-bool compel_cpu_has_feature(unsigned int feature)
-{
- fetch_rt_cpuinfo();
- return compel_test_cpu_cap(&rt_info, feature);
-}
-
-bool compel_fpu_has_feature(unsigned int feature)
-{
- fetch_rt_cpuinfo();
- return compel_test_fpu_cap(&rt_info, feature);
-}
-
-uint32_t compel_fpu_feature_size(unsigned int feature)
-{
- fetch_rt_cpuinfo();
- return 0;
-}
-
-uint32_t compel_fpu_feature_offset(unsigned int feature)
-{
- fetch_rt_cpuinfo();
- return 0;
-}
-
-void compel_cpu_clear_feature(unsigned int feature)
-{
- fetch_rt_cpuinfo();
- return compel_clear_cpu_cap(&rt_info, feature);
-}
-
-void compel_cpu_copy_cpuinfo(compel_cpuinfo_t *c)
-{
- fetch_rt_cpuinfo();
- memcpy(c, &rt_info, sizeof(rt_info));
-}
diff --git a/compel/arch/aarch64/src/lib/handle-elf-host.c b/compel/arch/aarch64/src/lib/handle-elf-host.c
deleted file mode 120000
index fe4611886..000000000
--- a/compel/arch/aarch64/src/lib/handle-elf-host.c
+++ /dev/null
@@ -1 +0,0 @@
-handle-elf.c
\ No newline at end of file
diff --git a/compel/arch/aarch64/src/lib/handle-elf.c b/compel/arch/aarch64/src/lib/handle-elf.c
deleted file mode 100644
index 206aef4cd..000000000
--- a/compel/arch/aarch64/src/lib/handle-elf.c
+++ /dev/null
@@ -1,32 +0,0 @@
-#include
-#include
-
-#include "handle-elf.h"
-#include "piegen.h"
-#include "log.h"
-
-static const unsigned char __maybe_unused elf_ident_64_le[EI_NIDENT] = {
- 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, /* clang-format */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static const unsigned char __maybe_unused elf_ident_64_be[EI_NIDENT] = {
- 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00, /* clang-format */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-int handle_binary(void *mem, size_t size)
-{
- const unsigned char *elf_ident =
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- elf_ident_64_le;
-#else
- elf_ident_64_be;
-#endif
-
- if (memcmp(mem, elf_ident, sizeof(elf_ident_64_le)) == 0)
- return handle_elf_aarch64(mem, size);
-
- pr_err("Unsupported Elf format detected\n");
- return -EINVAL;
-}
diff --git a/compel/arch/aarch64/src/lib/include/cpu.h b/compel/arch/aarch64/src/lib/include/cpu.h
deleted file mode 100644
index e69de29bb..000000000
diff --git a/compel/arch/aarch64/src/lib/include/handle-elf.h b/compel/arch/aarch64/src/lib/include/handle-elf.h
deleted file mode 100644
index 9f1a75081..000000000
--- a/compel/arch/aarch64/src/lib/include/handle-elf.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef COMPEL_HANDLE_ELF_H__
-#define COMPEL_HANDLE_ELF_H__
-
-#include "elf64-types.h"
-
-#define __handle_elf handle_elf_aarch64
-#define arch_is_machine_supported(e_machine) (e_machine == EM_AARCH64)
-
-extern int handle_elf_aarch64(void *mem, size_t size);
-
-#endif /* COMPEL_HANDLE_ELF_H__ */
diff --git a/compel/arch/aarch64/src/lib/include/syscall.h b/compel/arch/aarch64/src/lib/include/syscall.h
deleted file mode 100644
index 13ee906e1..000000000
--- a/compel/arch/aarch64/src/lib/include/syscall.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __COMPEL_SYSCALL_H__
-#define __COMPEL_SYSCALL_H__
-#define __NR(syscall, compat) \
- ({ \
- (void)compat; \
- __NR_##syscall; \
- })
-#endif
diff --git a/compel/arch/aarch64/src/lib/include/uapi/asm/.gitignore b/compel/arch/aarch64/src/lib/include/uapi/asm/.gitignore
deleted file mode 100644
index e69de29bb..000000000
diff --git a/compel/arch/aarch64/src/lib/include/uapi/asm/cpu.h b/compel/arch/aarch64/src/lib/include/uapi/asm/cpu.h
deleted file mode 100644
index 12e749508..000000000
--- a/compel/arch/aarch64/src/lib/include/uapi/asm/cpu.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef UAPI_COMPEL_ASM_CPU_H__
-#define UAPI_COMPEL_ASM_CPU_H__
-
-typedef struct {
-} compel_cpuinfo_t;
-
-#endif /* UAPI_COMPEL_ASM_CPU_H__ */
diff --git a/compel/arch/aarch64/src/lib/include/uapi/asm/gcs-types.h b/compel/arch/aarch64/src/lib/include/uapi/asm/gcs-types.h
deleted file mode 100644
index 9f9655e3b..000000000
--- a/compel/arch/aarch64/src/lib/include/uapi/asm/gcs-types.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef __UAPI_ASM_GCS_TYPES_H__
-#define __UAPI_ASM_GCS_TYPES_H__
-
-#ifndef NT_ARM_GCS
-#define NT_ARM_GCS 0x410 /* ARM GCS state */
-#endif
-
-/* Shadow Stack/Guarded Control Stack interface */
-#define PR_GET_SHADOW_STACK_STATUS 74
-#define PR_SET_SHADOW_STACK_STATUS 75
-#define PR_LOCK_SHADOW_STACK_STATUS 76
-
-/* When set PR_SHADOW_STACK_ENABLE flag allocates a Guarded Control Stack */
-#ifndef PR_SHADOW_STACK_ENABLE
-#define PR_SHADOW_STACK_ENABLE (1UL << 0)
-#endif
-
-/* Allows explicit GCS stores (eg. using GCSSTR) */
-#ifndef PR_SHADOW_STACK_WRITE
-#define PR_SHADOW_STACK_WRITE (1UL << 1)
-#endif
-
-/* Allows explicit GCS pushes (eg. using GCSPUSHM) */
-#ifndef PR_SHADOW_STACK_PUSH
-#define PR_SHADOW_STACK_PUSH (1UL << 2)
-#endif
-
-#ifndef SHADOW_STACK_SET_TOKEN
-#define SHADOW_STACK_SET_TOKEN 0x1 /* Set up a restore token in the shadow stack */
-#endif
-
-#define PR_SHADOW_STACK_ALL_MODES \
- PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE | PR_SHADOW_STACK_PUSH
-
-/* copied from: arch/arm64/include/asm/sysreg.h */
-#define GCS_CAP_VALID_TOKEN 0x1
-#define GCS_CAP_ADDR_MASK 0xFFFFFFFFFFFFF000ULL
-#define GCS_CAP(x) ((((unsigned long)x) & GCS_CAP_ADDR_MASK) | GCS_CAP_VALID_TOKEN)
-#define GCS_SIGNAL_CAP(addr) (((unsigned long)addr) & GCS_CAP_ADDR_MASK)
-
-#include
-
-#ifndef HWCAP_GCS
-#define HWCAP_GCS (1UL << 32)
-#endif
-
-#endif /* __UAPI_ASM_GCS_TYPES_H__ */
\ No newline at end of file
diff --git a/compel/arch/aarch64/src/lib/include/uapi/asm/infect-types.h b/compel/arch/aarch64/src/lib/include/uapi/asm/infect-types.h
deleted file mode 100644
index 606c92ffe..000000000
--- a/compel/arch/aarch64/src/lib/include/uapi/asm/infect-types.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#ifndef UAPI_COMPEL_ASM_TYPES_H__
-#define UAPI_COMPEL_ASM_TYPES_H__
-
-#include
-#include
-#include
-#include
-#include
-
-#define SIGMAX 64
-#define SIGMAX_OLD 31
-
-/*
- * Copied from the Linux kernel header arch/arm64/include/uapi/asm/ptrace.h
- *
- * A thread ARM CPU context
- */
-
-typedef struct user_pt_regs user_regs_struct_t;
-
-/*
- * GCS (Guarded Control Stack)
- *
- * This mirrors the kernel definition but renamed to cr_user_gcs
- * to avoid conflict with kernel headers (/usr/include/asm/ptrace.h).
- */
-struct cr_user_gcs {
- __u64 features_enabled;
- __u64 features_locked;
- __u64 gcspr_el0;
-};
-
-struct user_fpregs_struct {
- struct user_fpsimd_state fpstate;
- struct cr_user_gcs gcs;
-};
-typedef struct user_fpregs_struct user_fpregs_struct_t;
-
-#define __compel_arch_fetch_thread_area(tid, th) 0
-#define compel_arch_fetch_thread_area(tctl) 0
-#define compel_arch_get_tls_task(ctl, tls)
-#define compel_arch_get_tls_thread(tctl, tls)
-
-#define REG_RES(r) ((uint64_t)(r).regs[0])
-#define REG_IP(r) ((uint64_t)(r).pc)
-#define SET_REG_IP(r, val) ((r).pc = (val))
-#define REG_SP(r) ((uint64_t)((r).sp))
-#define REG_SYSCALL_NR(r) ((uint64_t)(r).regs[8])
-
-#define user_regs_native(pregs) true
-
-#define ARCH_SI_TRAP TRAP_BRKPT
-
-#define __NR(syscall, compat) \
- ({ \
- (void)compat; \
- __NR_##syscall; \
- })
-
-extern bool __compel_host_supports_gcs(void);
-#define compel_host_supports_gcs __compel_host_supports_gcs
-
-struct parasite_ctl;
-extern int __parasite_setup_shstk(struct parasite_ctl *ctl,
- user_fpregs_struct_t *ext_regs);
-#define parasite_setup_shstk __parasite_setup_shstk
-
-#endif /* UAPI_COMPEL_ASM_TYPES_H__ */
diff --git a/compel/arch/aarch64/src/lib/include/uapi/asm/processor-flags.h b/compel/arch/aarch64/src/lib/include/uapi/asm/processor-flags.h
deleted file mode 100644
index 15719184a..000000000
--- a/compel/arch/aarch64/src/lib/include/uapi/asm/processor-flags.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__
-#define UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__
-
-#endif /* UAPI_COMPEL_ASM_PROCESSOR_FLAGS_H__ */
diff --git a/compel/arch/aarch64/src/lib/include/uapi/asm/sigframe.h b/compel/arch/aarch64/src/lib/include/uapi/asm/sigframe.h
deleted file mode 100644
index d3a46c2b9..000000000
--- a/compel/arch/aarch64/src/lib/include/uapi/asm/sigframe.h
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef UAPI_COMPEL_ASM_SIGFRAME_H__
-#define UAPI_COMPEL_ASM_SIGFRAME_H__
-
-#include
-#include
-
-#include
-#include
-
-/* Copied from the kernel header arch/arm64/include/uapi/asm/sigcontext.h */
-
-#define FPSIMD_MAGIC 0x46508001
-#define GCS_MAGIC 0x47435300
-
-typedef struct fpsimd_context fpu_state_t;
-
-struct cr_gcs_context {
- struct _aarch64_ctx head;
- __u64 gcspr;
- __u64 features_enabled;
- __u64 reserved;
-};
-
-struct aux_context {
- struct fpsimd_context fpsimd;
- struct cr_gcs_context gcs;
- /* additional context to be added before "end" */
- struct _aarch64_ctx end;
-};
-
-// XXX: the identifier rt_sigcontext is expected to be struct by the CRIU code
-#define rt_sigcontext sigcontext
-
-#include
-
-/* Copied from the kernel source arch/arm64/kernel/signal.c */
-
-struct rt_sigframe {
- siginfo_t info;
- ucontext_t uc;
- uint64_t fp;
- uint64_t lr;
-};
-
-/* clang-format off */
-#define ARCH_RT_SIGRETURN(new_sp, rt_sigframe) \
- asm volatile( \
- "mov sp, %0 \n" \
- "mov x8, #"__stringify(__NR_rt_sigreturn)" \n" \
- "svc #0 \n" \
- : \
- : "r"(new_sp) \
- : "x8", "memory")
-/* clang-format on */
-
-/* cr_sigcontext is copied from arch/arm64/include/uapi/asm/sigcontext.h */
-struct cr_sigcontext {
- __u64 fault_address;
- /* AArch64 registers */
- __u64 regs[31];
- __u64 sp;
- __u64 pc;
- __u64 pstate;
- /* 4K reserved for FP/SIMD state and future expansion */
- __u8 __reserved[4096] __attribute__((__aligned__(16)));
-};
-
-#define RT_SIGFRAME_UC(rt_sigframe) (&rt_sigframe->uc)
-#define RT_SIGFRAME_REGIP(rt_sigframe) ((long unsigned int)(rt_sigframe)->uc.uc_mcontext.pc)
-#define RT_SIGFRAME_HAS_FPU(rt_sigframe) (1)
-#define RT_SIGFRAME_SIGCONTEXT(rt_sigframe) ((struct cr_sigcontext *)&(rt_sigframe)->uc.uc_mcontext)
-#define RT_SIGFRAME_AUX_CONTEXT(rt_sigframe) ((struct aux_context *)&(RT_SIGFRAME_SIGCONTEXT(rt_sigframe)->__reserved))
-#define RT_SIGFRAME_FPU(rt_sigframe) (&RT_SIGFRAME_AUX_CONTEXT(rt_sigframe)->fpsimd)
-#define RT_SIGFRAME_OFFSET(rt_sigframe) 0
-#define RT_SIGFRAME_GCS(rt_sigframe) (&RT_SIGFRAME_AUX_CONTEXT(rt_sigframe)->gcs)
-
-#define rt_sigframe_erase_sigset(sigframe) memset(&sigframe->uc.uc_sigmask, 0, sizeof(k_rtsigset_t))
-#define rt_sigframe_copy_sigset(sigframe, from) memcpy(&sigframe->uc.uc_sigmask, from, sizeof(k_rtsigset_t))
-
-#endif /* UAPI_COMPEL_ASM_SIGFRAME_H__ */
diff --git a/compel/arch/aarch64/src/lib/infect.c b/compel/arch/aarch64/src/lib/infect.c
deleted file mode 100644
index b9ca00afb..000000000
--- a/compel/arch/aarch64/src/lib/infect.c
+++ /dev/null
@@ -1,320 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include "common/page.h"
-#include "uapi/compel/asm/infect-types.h"
-#include "log.h"
-#include "errno.h"
-#include "infect.h"
-#include "infect-priv.h"
-#include "asm/gcs-types.h"
-#include
-
-unsigned __page_size = 0;
-unsigned __page_shift = 0;
-
-/*
- * Injected syscall instruction
- */
-const char code_syscall[] = {
- 0x01, 0x00, 0x00, 0xd4, /* SVC #0 */
- 0x00, 0x00, 0x20, 0xd4 /* BRK #0 */
-};
-
-static const int code_syscall_aligned = round_up(sizeof(code_syscall), sizeof(long));
-
-static inline void __always_unused __check_code_syscall(void)
-{
- BUILD_BUG_ON(code_syscall_aligned != BUILTIN_SYSCALL_SIZE);
- BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
-}
-
-bool __compel_host_supports_gcs(void)
-{
- unsigned long hwcap = getauxval(AT_HWCAP);
- return (hwcap & HWCAP_GCS) != 0;
-}
-
-static bool __compel_gcs_enabled(struct cr_user_gcs *gcs)
-{
- if (!compel_host_supports_gcs())
- return false;
-
- return gcs && (gcs->features_enabled & PR_SHADOW_STACK_ENABLE) != 0;
-}
-
-int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe, user_regs_struct_t *regs, user_fpregs_struct_t *fpregs)
-{
- struct fpsimd_context *fpsimd = RT_SIGFRAME_FPU(sigframe);
- struct cr_gcs_context *gcs = RT_SIGFRAME_GCS(sigframe);
-
- memcpy(sigframe->uc.uc_mcontext.regs, regs->regs, sizeof(regs->regs));
-
- pr_debug("sigreturn_prep_regs_plain: sp %lx pc %lx\n", (long)regs->sp, (long)regs->pc);
-
- sigframe->uc.uc_mcontext.sp = regs->sp;
- sigframe->uc.uc_mcontext.pc = regs->pc;
- sigframe->uc.uc_mcontext.pstate = regs->pstate;
-
- memcpy(fpsimd->vregs, fpregs->fpstate.vregs, 32 * sizeof(__uint128_t));
-
- fpsimd->fpsr = fpregs->fpstate.fpsr;
- fpsimd->fpcr = fpregs->fpstate.fpcr;
-
- fpsimd->head.magic = FPSIMD_MAGIC;
- fpsimd->head.size = sizeof(*fpsimd);
-
- if (__compel_gcs_enabled(&fpregs->gcs)) {
- gcs->head.magic = GCS_MAGIC;
- gcs->head.size = sizeof(*gcs);
- gcs->reserved = 0;
- gcs->gcspr = fpregs->gcs.gcspr_el0 - 8;
- gcs->features_enabled = fpregs->gcs.features_enabled;
-
- pr_debug("sigframe gcspr=%llx features_enabled=%llx\n", fpregs->gcs.gcspr_el0 - 8, fpregs->gcs.features_enabled);
- } else {
- pr_debug("sigframe gcspr=[disabled]\n");
- memset(gcs, 0, sizeof(*gcs));
- }
-
- return 0;
-}
-
-int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe, struct rt_sigframe *rsigframe)
-{
- return 0;
-}
-
-int compel_get_task_regs(pid_t pid, user_regs_struct_t *regs, user_fpregs_struct_t *ext_regs, save_regs_t save,
- void *arg, __maybe_unused unsigned long flags)
-{
- struct iovec iov;
- int ret;
-
- pr_info("Dumping GP/FPU registers for %d\n", pid);
-
- iov.iov_base = regs;
- iov.iov_len = sizeof(user_regs_struct_t);
- if ((ret = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))) {
- pr_perror("Failed to obtain CPU registers for %d", pid);
- goto err;
- }
-
- iov.iov_base = &ext_regs->fpstate;
- iov.iov_len = sizeof(ext_regs->fpstate);
- if ((ret = ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov))) {
- pr_perror("Failed to obtain FPU registers for %d", pid);
- goto err;
- }
-
- memset(&ext_regs->gcs, 0, sizeof(ext_regs->gcs));
-
- iov.iov_base = &ext_regs->gcs;
- iov.iov_len = sizeof(ext_regs->gcs);
- if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_GCS, &iov) == 0) {
- pr_info("gcs: GCSPR_EL0 for %d: 0x%llx, features: 0x%llx\n",
- pid, ext_regs->gcs.gcspr_el0, ext_regs->gcs.features_enabled);
-
- if (!__compel_gcs_enabled(&ext_regs->gcs))
- pr_info("gcs: GCS is NOT enabled\n");
- } else {
- pr_info("gcs: GCS state not available for %d\n", pid);
- }
-
- ret = save(pid, arg, regs, ext_regs);
-err:
- return ret;
-}
-
-int compel_set_task_ext_regs(pid_t pid, user_fpregs_struct_t *ext_regs)
-{
- struct iovec iov;
-
- struct cr_user_gcs gcs;
- struct iovec gcs_iov = { .iov_base = &gcs, .iov_len = sizeof(gcs) };
-
- pr_info("Restoring GP/FPU registers for %d\n", pid);
-
- iov.iov_base = &ext_regs->fpstate;
- iov.iov_len = sizeof(ext_regs->fpstate);
- if (ptrace(PTRACE_SETREGSET, pid, NT_PRFPREG, &iov)) {
- pr_perror("Failed to set FPU registers for %d", pid);
- return -1;
- }
-
- if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_GCS, &gcs_iov) < 0) {
- pr_warn("gcs: Failed to get GCS for %d\n", pid);
- } else {
- ext_regs->gcs = gcs;
- compel_set_task_gcs_regs(pid, ext_regs);
- }
-
- return 0;
-}
-
-int compel_set_task_gcs_regs(pid_t pid, user_fpregs_struct_t *ext_regs)
-{
- struct iovec iov;
-
- pr_info("gcs: restoring GCS registers for %d\n", pid);
- pr_info("gcs: restoring GCS: gcspr=%llx features=%llx\n",
- ext_regs->gcs.gcspr_el0, ext_regs->gcs.features_enabled);
-
- iov.iov_base = &ext_regs->gcs;
- iov.iov_len = sizeof(ext_regs->gcs);
-
- if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_GCS, &iov)) {
- pr_perror("gcs: Failed to set GCS registers for %d", pid);
- return -1;
- }
-
- return 0;
-}
-
-int compel_syscall(struct parasite_ctl *ctl, int nr, long *ret, unsigned long arg1, unsigned long arg2,
- unsigned long arg3, unsigned long arg4, unsigned long arg5, unsigned long arg6)
-{
- user_regs_struct_t regs = ctl->orig.regs;
- int err;
-
- regs.regs[8] = (unsigned long)nr;
- regs.regs[0] = arg1;
- regs.regs[1] = arg2;
- regs.regs[2] = arg3;
- regs.regs[3] = arg4;
- regs.regs[4] = arg5;
- regs.regs[5] = arg6;
- regs.regs[6] = 0;
- regs.regs[7] = 0;
-
- err = compel_execute_syscall(ctl, ®s, code_syscall);
-
- *ret = regs.regs[0];
- return err;
-}
-
-void *remote_mmap(struct parasite_ctl *ctl, void *addr, size_t length, int prot, int flags, int fd, off_t offset)
-{
- long map;
- int err;
-
- err = compel_syscall(ctl, __NR_mmap, &map, (unsigned long)addr, length, prot, flags, fd, offset);
- if (err < 0 || (long)map < 0)
- map = 0;
-
- return (void *)map;
-}
-
-void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *regs)
-{
- regs->pc = new_ip;
- if (stack)
- regs->sp = (unsigned long)stack;
-}
-
-bool arch_can_dump_task(struct parasite_ctl *ctl)
-{
- /*
- * TODO: Add proper check here
- */
- return true;
-}
-
-int arch_fetch_sas(struct parasite_ctl *ctl, struct rt_sigframe *s)
-{
- long ret;
- int err;
-
- err = compel_syscall(ctl, __NR_sigaltstack, &ret, 0, (unsigned long)&s->uc.uc_stack, 0, 0, 0, 0);
- return err ? err : ret;
-}
-
-/*
- * Range for task size calculated from the following Linux kernel files:
- * arch/arm64/include/asm/memory.h
- * arch/arm64/Kconfig
- *
- * TODO: handle 32 bit tasks
- */
-#define TASK_SIZE_MIN (1UL << 39)
-#define TASK_SIZE_MAX (1UL << 48)
-
-unsigned long compel_task_size(void)
-{
- unsigned long task_size;
-
- for (task_size = TASK_SIZE_MIN; task_size < TASK_SIZE_MAX; task_size <<= 1)
- if (munmap((void *)task_size, page_size()))
- break;
- return task_size;
-}
-
-int inject_gcs_cap_token(struct parasite_ctl *ctl, pid_t pid, struct cr_user_gcs *gcs)
-{
- struct iovec gcs_iov = { .iov_base = gcs, .iov_len = sizeof(*gcs) };
-
- uint64_t token_addr = gcs->gcspr_el0 - 8;
- uint64_t sigtramp_addr = gcs->gcspr_el0 - 16;
-
- uint64_t cap_token = ALIGN_DOWN(GCS_SIGNAL_CAP(token_addr), 8);
- unsigned long restorer_addr;
-
- pr_info("gcs: (setup) CAP token: 0x%lx at addr: 0x%lx\n", cap_token, token_addr);
-
- /* Inject capability token at gcspr_el0 - 8 */
- if (ptrace(PTRACE_POKEDATA, pid, (void *)token_addr, cap_token)) {
- pr_perror("gcs: (setup) Inject GCS cap token failed");
- return -1;
- }
-
- /* Inject restorer trampoline address (gcspr_el0 - 16) */
- restorer_addr = ctl->parasite_ip;
- if (ptrace(PTRACE_POKEDATA, pid, (void *)sigtramp_addr, restorer_addr)) {
- pr_perror("gcs: (setup) Inject GCS restorer failed");
- return -1;
- }
-
- /* Update GCSPR_EL0 */
- gcs->gcspr_el0 = token_addr;
- if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_GCS, &gcs_iov)) {
- pr_perror("gcs: PTRACE_SETREGS FAILED");
- return -1;
- }
-
- pr_debug("gcs: parasite_ip=%#lx sp=%#llx gcspr_el0=%#llx\n",
- ctl->parasite_ip, ctl->orig.regs.sp, gcs->gcspr_el0);
-
- return 0;
-}
-
-int parasite_setup_shstk(struct parasite_ctl *ctl, user_fpregs_struct_t *ext_regs)
-{
- struct cr_user_gcs gcs;
- struct iovec gcs_iov = { .iov_base = &gcs, .iov_len = sizeof(gcs) };
- pid_t pid = ctl->rpid;
-
- if(!__compel_host_supports_gcs())
- return 0;
-
- if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_GCS, &gcs_iov) != 0) {
- pr_perror("GCS state not available for %d", pid);
- return -1;
- }
-
- if (!__compel_gcs_enabled(&gcs))
- return 0;
-
- if (inject_gcs_cap_token(ctl, pid, &gcs)) {
- pr_perror("Failed to inject GCS cap token for %d", pid);
- return -1;
- }
-
- pr_info("gcs: GCS enabled for %d\n", pid);
-
- return 0;
-}
diff --git a/compel/arch/arm/plugins/include/asm/prologue.h b/compel/arch/arm/plugins/include/asm/prologue.h
deleted file mode 120000
index e0275e350..000000000
--- a/compel/arch/arm/plugins/include/asm/prologue.h
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../arch/x86/plugins/include/asm/prologue.h
\ No newline at end of file
diff --git a/compel/arch/arm/plugins/include/asm/syscall-types.h b/compel/arch/arm/plugins/include/asm/syscall-types.h
deleted file mode 100644
index acc03de52..000000000
--- a/compel/arch/arm/plugins/include/asm/syscall-types.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef COMPEL_ARCH_SYSCALL_TYPES_H__
-#define COMPEL_ARCH_SYSCALL_TYPES_H__
-
-#define SA_RESTORER 0x04000000
-
-typedef void rt_signalfn_t(int, siginfo_t *, void *);
-typedef rt_signalfn_t *rt_sighandler_t;
-
-typedef void rt_restorefn_t(void);
-typedef rt_restorefn_t *rt_sigrestore_t;
-
-#define _KNSIG 64
-#define _NSIG_BPW 32
-
-#define _KNSIG_WORDS (_KNSIG / _NSIG_BPW)
-
-typedef struct {
- unsigned long sig[_KNSIG_WORDS];
-} k_rtsigset_t;
-
-typedef struct {
- rt_sighandler_t rt_sa_handler;
- unsigned long rt_sa_flags;
- rt_sigrestore_t rt_sa_restorer;
- k_rtsigset_t rt_sa_mask;
-} rt_sigaction_t;
-
-#endif /* COMPEL_ARCH_SYSCALL_TYPES_H__ */
diff --git a/compel/arch/arm/plugins/include/features.h b/compel/arch/arm/plugins/include/features.h
deleted file mode 100644
index b4a3cded2..000000000
--- a/compel/arch/arm/plugins/include/features.h
+++ /dev/null
@@ -1,4 +0,0 @@
-#ifndef __COMPEL_ARCH_FEATURES_H
-#define __COMPEL_ARCH_FEATURES_H
-
-#endif /* __COMPEL_ARCH_FEATURES_H */
diff --git a/compel/arch/arm/plugins/std/parasite-head.S b/compel/arch/arm/plugins/std/parasite-head.S
deleted file mode 100644
index 6e46bed1f..000000000
--- a/compel/arch/arm/plugins/std/parasite-head.S
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "common/asm/linkage.h"
-
- .section .head.text, "ax"
-ENTRY(__export_parasite_head_start)
- bl parasite_service
- .byte 0xf0, 0x01, 0xf0, 0xe7 @ the instruction UDF #32 generates the signal SIGTRAP in Linux
-
-END(__export_parasite_head_start)
diff --git a/compel/arch/arm/plugins/std/syscalls/Makefile.syscalls b/compel/arch/arm/plugins/std/syscalls/Makefile.syscalls
deleted file mode 100644
index c89f1a56a..000000000
--- a/compel/arch/arm/plugins/std/syscalls/Makefile.syscalls
+++ /dev/null
@@ -1,59 +0,0 @@
-ccflags-y += -iquote $(PLUGIN_ARCH_DIR)/std/syscalls/
-asflags-y += -iquote $(PLUGIN_ARCH_DIR)/std/syscalls/
-
-sys-types := $(obj)/include/uapi/std/syscall-types.h
-sys-codes := $(obj)/include/uapi/std/syscall-codes.h
-sys-proto := $(obj)/include/uapi/std/syscall.h
-
-sys-def := $(PLUGIN_ARCH_DIR)/std/syscalls/syscall.def
-sys-asm-common-name := std/syscalls/syscall-common.S
-sys-asm-common := $(PLUGIN_ARCH_DIR)/$(sys-asm-common-name)
-sys-asm-types := $(obj)/include/uapi/std/asm/syscall-types.h
-sys-exec-tbl = $(PLUGIN_ARCH_DIR)/std/sys-exec-tbl.c
-
-sys-gen := $(PLUGIN_ARCH_DIR)/std/syscalls/gen-syscalls.pl
-sys-gen-tbl := $(PLUGIN_ARCH_DIR)/std/syscalls/gen-sys-exec-tbl.pl
-
-sys-asm := ./$(PLUGIN_ARCH_DIR)/std/syscalls/syscalls.S
-std-lib-y += $(sys-asm:.S=).o
-
-ifeq ($(ARCH),arm)
-arch_bits := 32
-else
-arch_bits := 64
-endif
-
-sys-exec-tbl := sys-exec-tbl.c
-
-$(sys-asm) $(sys-types) $(sys-codes) $(sys-proto): $(sys-gen) $(sys-def) $(sys-asm-common) $(sys-asm-types)
- $(E) " GEN " $@
- $(Q) perl \
- $(sys-gen) \
- $(sys-def) \
- $(sys-codes) \
- $(sys-proto) \
- $(sys-asm) \
- $(sys-asm-common-name) \
- $(sys-types) \
- $(arch_bits)
-
-$(sys-asm:.S=).o: $(sys-asm)
-
-$(sys-exec-tbl): $(sys-gen-tbl) $(sys-def)
- $(E) " GEN " $@
- $(Q) perl \
- $(sys-gen-tbl) \
- $(sys-def) \
- $(sys-exec-tbl) \
- $(arch_bits)
-
-$(sys-asm-types): $(PLUGIN_ARCH_DIR)/include/asm/syscall-types.h
- $(call msg-gen, $@)
- $(Q) ln -s ../../../../../../$(PLUGIN_ARCH_DIR)/include/asm/syscall-types.h $(sys-asm-types)
- $(Q) ln -s ../../../../../$(PLUGIN_ARCH_DIR)/std/syscalls/syscall-aux.S $(obj)/include/uapi/std/syscall-aux.S
- $(Q) ln -s ../../../../../$(PLUGIN_ARCH_DIR)/std/syscalls/syscall-aux.h $(obj)/include/uapi/std/syscall-aux.h
-
-std-headers-deps += $(sys-asm) $(sys-codes) $(sys-proto) $(sys-asm-types)
-mrproper-y += $(std-headers-deps)
-mrproper-y += $(obj)/include/uapi/std/syscall-aux.S
-mrproper-y += $(obj)/include/uapi/std/syscall-aux.h
diff --git a/compel/arch/arm/plugins/std/syscalls/gen-sys-exec-tbl.pl b/compel/arch/arm/plugins/std/syscalls/gen-sys-exec-tbl.pl
deleted file mode 100755
index 2f90c13fe..000000000
--- a/compel/arch/arm/plugins/std/syscalls/gen-sys-exec-tbl.pl
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-
-my $in = $ARGV[0];
-my $tblout = $ARGV[1];
-my $bits = $ARGV[2];
-
-my $code = "code$bits";
-
-open TBLOUT, ">", $tblout or die $!;
-open IN, "<", $in or die $!;
-
-print TBLOUT "/* Autogenerated, don't edit */\n";
-print TBLOUT "static struct syscall_exec_desc sc_exec_table[] = {\n";
-
-for () {
- if ($_ =~ /\#/) {
- next;
- }
-
- my $sys_name;
- my $sys_num;
-
- if (/(?\S+)\s+(?\S+)\s+(?\d+|\!)\s+(?(?:\d+|\!))\s+\((?.+)\)/) {
- $sys_name = $+{alias};
- } elsif (/(?\S+)\s+(?\d+|\!)\s+(?(?:\d+|\!))\s+\((?.+)\)/) {
- $sys_name = $+{name};
- } else {
- unlink $tblout;
- die "Invalid syscall definition file: invalid entry $_\n";
- }
-
- $sys_num = $+{$code};
-
- if ($sys_num ne "!") {
- print TBLOUT "SYSCALL($sys_name, $sys_num)\n";
- }
-}
-
-print TBLOUT " { }, /* terminator */";
-print TBLOUT "};"
diff --git a/compel/arch/arm/plugins/std/syscalls/gen-syscalls.pl b/compel/arch/arm/plugins/std/syscalls/gen-syscalls.pl
deleted file mode 100755
index a0942114d..000000000
--- a/compel/arch/arm/plugins/std/syscalls/gen-syscalls.pl
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-
-my $in = $ARGV[0];
-my $codesout = $ARGV[1];
-my $codes = $ARGV[1];
-$codes =~ s/.*include\/uapi\//compel\/plugins\//g;
-my $protosout = $ARGV[2];
-my $protos = $ARGV[2];
-$protos =~ s/.*include\/uapi\//compel\/plugins\//g;
-my $asmout = $ARGV[3];
-my $asmcommon = $ARGV[4];
-my $prototypes = $ARGV[5];
-$prototypes =~ s/.*include\/uapi\//compel\/plugins\//g;
-my $bits = $ARGV[6];
-
-my $codesdef = $codes;
-$codesdef =~ tr/.\-\//_/;
-my $protosdef = $protos;
-$protosdef =~ tr/.\-\//_/;
-my $code = "code$bits";
-my $need_aux = 0;
-
-unlink $codesout;
-unlink $protosout;
-unlink $asmout;
-
-open CODESOUT, ">", $codesout or die $!;
-open PROTOSOUT, ">", $protosout or die $!;
-open ASMOUT, ">", $asmout or die $!;
-open IN, "<", $in or die $!;
-
-print CODESOUT <<"END";
-/* Autogenerated, don't edit */
-#ifndef $codesdef
-#define $codesdef
-END
-
-print PROTOSOUT <<"END";
-/* Autogenerated, don't edit */
-#ifndef $protosdef
-#define $protosdef
-#include <$prototypes>
-#include <$codes>
-END
-
-print ASMOUT <<"END";
-/* Autogenerated, don't edit */
-#include <$codes>
-#include "$asmcommon"
-END
-
-
-for () {
- if ($_ =~ /\#/) {
- next;
- }
-
- my $code_macro;
- my $sys_macro;
- my $sys_name;
-
- if (/(?\S+)\s+(?\S+)\s+(?\d+|\!)\s+(?(?:\d+|\!))\s+\((?.+)\)/) {
- $code_macro = "__NR_$+{name}";
- $sys_macro = "SYS_$+{name}";
- $sys_name = "sys_$+{alias}";
- } elsif (/(?\S+)\s+(?\d+|\!)\s+(?(?:\d+|\!))\s+\((?.+)\)/) {
- $code_macro = "__NR_$+{name}";
- $sys_macro = "SYS_$+{name}";
- $sys_name = "sys_$+{name}";
- } else {
- unlink $codesout;
- unlink $protosout;
- unlink $asmout;
-
- die "Invalid syscall definition file: invalid entry $_\n";
- }
-
- if ($+{$code} ne "!") {
- print CODESOUT "#ifndef $code_macro\n#define $code_macro $+{$code}\n#endif\n";
- print CODESOUT "#ifndef $sys_macro\n#define $sys_macro $code_macro\n#endif\n";
- print ASMOUT "syscall $sys_name, $code_macro\n";
-
- } else {
- $need_aux = 1;
- }
-
- print PROTOSOUT "extern long $sys_name($+{args});\n";
-}
-
-if ($need_aux == 1) {
- print ASMOUT "#include \n";
- print CODESOUT "#include \n";
-}
-
-print CODESOUT "#endif /* $codesdef */";
-print PROTOSOUT "#endif /* $protosdef */";
diff --git a/compel/arch/arm/plugins/std/syscalls/syscall-aux.S b/compel/arch/arm/plugins/std/syscalls/syscall-aux.S
deleted file mode 100644
index 22cc328c1..000000000
--- a/compel/arch/arm/plugins/std/syscalls/syscall-aux.S
+++ /dev/null
@@ -1,13 +0,0 @@
-nr_sys_mmap:
- .long 192
-
-ENTRY(sys_mmap)
- push {r4, r5, r7, lr}
- ldr r4, [sp, #16]
- ldr r5, [sp, #20]
- lsr r5, #12
- adr r7, nr_sys_mmap
- ldr r7, [r7]
- svc 0x00000000
- pop {r4, r5, r7, pc}
-END(sys_mmap)
diff --git a/compel/arch/arm/plugins/std/syscalls/syscall-aux.h b/compel/arch/arm/plugins/std/syscalls/syscall-aux.h
deleted file mode 100644
index 7418546e1..000000000
--- a/compel/arch/arm/plugins/std/syscalls/syscall-aux.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef __NR_mmap2
-#define __NR_mmap2 192
-#endif
-
-#ifndef __ARM_NR_BASE
-#define __ARM_NR_BASE 0x0f0000
-#endif
-
-#ifndef __ARM_NR_breakpoint
-#define __ARM_NR_breakpoint (__ARM_NR_BASE + 1)
-#endif
-
-#ifndef __ARM_NR_cacheflush
-#define __ARM_NR_cacheflush (__ARM_NR_BASE + 2)
-#endif
-
-#ifndef __ARM_NR_usr26
-#define __ARM_NR_usr26 (__ARM_NR_BASE + 3)
-#endif
-
-#ifndef __ARM_NR_usr32
-#define __ARM_NR_usr32 (__ARM_NR_BASE + 4)
-#endif
-
-#ifndef __ARM_NR_set_tls
-#define __ARM_NR_set_tls (__ARM_NR_BASE + 5)
-#endif
diff --git a/compel/arch/arm/plugins/std/syscalls/syscall-common.S b/compel/arch/arm/plugins/std/syscalls/syscall-common.S
deleted file mode 100644
index 9ac53bdd2..000000000
--- a/compel/arch/arm/plugins/std/syscalls/syscall-common.S
+++ /dev/null
@@ -1,34 +0,0 @@
-#include "common/asm/linkage.h"
-
-@ We use the register R8 unlike libc that uses R12.
-@ This avoids corruption of the register by the stub
-@ for the syscall sys_munmap() when syscalls are hooked
-@ by ptrace(). However we have to make sure that
-@ the compiler doesn't use the register on the route
-@ between parasite_service() and sys_munmap().
-
-syscall_common:
- ldr r7, [r7]
- add r8, sp, #24
- ldm r8, {r4, r5, r6}
- svc 0x00000000
- pop {r4, r5, r6, r7, r8, pc}
-
-
-.macro syscall name, nr
- .nr_\name :
- .long \nr
-
- ENTRY(\name)
- push {r4, r5, r6, r7, r8, lr}
- adr r7, .nr_\name
- b syscall_common
- END(\name)
-.endm
-
-
-ENTRY(__cr_restore_rt)
- adr r7, .nr_sys_rt_sigreturn
- ldr r7, [r7]
- svc #0
-END(__cr_restore_rt)
diff --git a/compel/arch/arm/plugins/std/syscalls/syscall.def b/compel/arch/arm/plugins/std/syscalls/syscall.def
deleted file mode 100644
index ffe59be13..000000000
--- a/compel/arch/arm/plugins/std/syscalls/syscall.def
+++ /dev/null
@@ -1,128 +0,0 @@
-#
-# System calls table, please make sure the table consists of only the syscalls
-# really used somewhere in the project.
-#
-# The template is (name and arguments are optional if you need only __NR_x
-# defined, but no real entry point in syscalls lib).
-#
-# name/alias code64 code32 arguments
-# -----------------------------------------------------------------------
-#
-read 63 3 (int fd, void *buf, unsigned long count)
-write 64 4 (int fd, const void *buf, unsigned long count)
-open ! 5 (const char *filename, unsigned long flags, unsigned long mode)
-close 57 6 (int fd)
-lseek 62 19 (int fd, unsigned long offset, unsigned long origin)
-mmap 222 ! (void *addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long offset)
-mprotect 226 125 (const void *addr, unsigned long len, unsigned long prot)
-munmap 215 91 (void *addr, unsigned long len)
-brk 214 45 (void *addr)
-rt_sigaction sigaction 134 174 (int signum, const rt_sigaction_t *act, rt_sigaction_t *oldact, size_t sigsetsize)
-rt_sigprocmask sigprocmask 135 175 (int how, k_rtsigset_t *set, k_rtsigset_t *old, size_t sigsetsize)
-rt_sigreturn 139 173 (void)
-ioctl 29 54 (unsigned int fd, unsigned int cmd, unsigned long arg)
-pread64 67 180 (unsigned int fd, char *buf, size_t count, loff_t pos)
-ptrace 117 26 (long request, pid_t pid, void *addr, void *data)
-mremap 216 163 (unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flag, unsigned long new_addr)
-mincore 232 219 (void *addr, unsigned long size, unsigned char *vec)
-madvise 233 220 (unsigned long start, size_t len, int behavior)
-shmat 196 305 (int shmid, void *shmaddr, int shmflag)
-pause 1061 29 (void)
-nanosleep 101 162 (struct timespec *req, struct timespec *rem)
-getitimer 102 105 (int which, const struct itimerval *val)
-setitimer 103 104 (int which, const struct itimerval *val, struct itimerval *old)
-getpid 172 20 (void)
-socket 198 281 (int domain, int type, int protocol)
-connect 203 283 (int sockfd, struct sockaddr *addr, int addrlen)
-sendto 206 290 (int sockfd, void *buff, size_t len, unsigned int flags, struct sockaddr *addr, int addr_len)
-recvfrom 207 292 (int sockfd, void *ubuf, size_t size, unsigned int flags, struct sockaddr *addr, int *addr_len)
-sendmsg 211 296 (int sockfd, const struct msghdr *msg, int flags)
-recvmsg 212 297 (int sockfd, struct msghdr *msg, int flags)
-shutdown 210 293 (int sockfd, int how)
-bind 200 282 (int sockfd, const struct sockaddr *addr, int addrlen)
-setsockopt 208 294 (int sockfd, int level, int optname, const void *optval, socklen_t optlen)
-getsockopt 209 295 (int sockfd, int level, int optname, const void *optval, socklen_t *optlen)
-clone 220 120 (unsigned long flags, void *child_stack, void *parent_tid, unsigned long newtls, void *child_tid)
-exit 93 1 (unsigned long error_code)
-wait4 260 114 (int pid, int *status, int options, struct rusage *ru)
-waitid 95 280 (int which, pid_t pid, struct siginfo *infop, int options, struct rusage *ru)
-kill 129 37 (long pid, int sig)
-fcntl 25 55 (int fd, int type, long arg)
-flock 32 143 (int fd, unsigned long cmd)
-mkdir ! 39 (const char *name, int mode)
-rmdir ! 40 (const char *name)
-unlink ! 10 (char *pathname)
-readlinkat 78 332 (int fd, const char *path, char *buf, int bufsize)
-umask 166 60 (int mask)
-getgroups 158 205 (int gsize, unsigned int *groups)
-setgroups 159 206 (int gsize, unsigned int *groups)
-setresuid 147 164 (int uid, int euid, int suid)
-getresuid 148 165 (int *uid, int *euid, int *suid)
-setresgid 149 170 (int gid, int egid, int sgid)
-getresgid 150 171 (int *gid, int *egid, int *sgid)
-getpgid 155 132 (pid_t pid)
-setfsuid 151 138 (int fsuid)
-setfsgid 152 139 (int fsgid)
-getsid 156 147 (void)
-capget 90 184 (struct cap_header *h, struct cap_data *d)
-capset 91 185 (struct cap_header *h, struct cap_data *d)
-rt_sigqueueinfo 138 178 (pid_t pid, int sig, siginfo_t *info)
-setpriority 140 97 (int which, int who, int nice)
-sched_setscheduler 119 156 (int pid, int policy, struct sched_param *p)
-sigaltstack 132 186 (const void *uss, void *uoss)
-personality 92 136 (unsigned int personality)
-prctl 167 172 (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5)
-arch_prctl ! 17 (int option, unsigned long addr)
-setrlimit 164 75 (int resource, struct krlimit *rlim)
-mount 40 21 (char *dev_nmae, char *dir_name, char *type, unsigned long flags, void *data)
-umount2 39 52 (char *name, int flags)
-gettid 178 224 (void)
-futex 98 240 (uint32_t *uaddr, int op, uint32_t val, struct timespec *utime, uint32_t *uaddr2, uint32_t val3)
-set_tid_address 96 256 (int *tid_addr)
-restart_syscall 128 0 (void)
-timer_create 107 257 (clockid_t which_clock, struct sigevent *timer_event_spec, kernel_timer_t *created_timer_id)
-timer_settime 110 258 (kernel_timer_t timer_id, int flags, const struct itimerspec *new_setting, struct itimerspec *old_setting)
-timer_gettime 108 259 (int timer_id, const struct itimerspec *setting)
-timer_getoverrun 109 260 (int timer_id)
-timer_delete 111 261 (kernel_timer_t timer_id)
-clock_gettime 113 263 (clockid_t which_clock, struct timespec *tp)
-exit_group 94 248 (int error_code)
-set_robust_list 99 338 (struct robust_list_head *head, size_t len)
-get_robust_list 100 339 (int pid, struct robust_list_head **head_ptr, size_t *len_ptr)
-signalfd4 74 355 (int fd, k_rtsigset_t *mask, size_t sizemask, int flags)
-rt_tgsigqueueinfo 240 363 (pid_t tgid, pid_t pid, int sig, siginfo_t *info)
-vmsplice 75 343 (int fd, const struct iovec *iov, unsigned long nr_segs, unsigned int flags)
-timerfd_settime 86 353 (int ufd, int flags, const struct itimerspec *utmr, struct itimerspec *otmr)
-fanotify_init 262 367 (unsigned int flags, unsigned int event_f_flags)
-fanotify_mark 263 368 (int fanotify_fd, unsigned int flags, uint64_t mask, int dfd, const char *pathname)
-open_by_handle_at 265 371 (int mountdirfd, struct file_handle *handle, int flags)
-setns 268 375 (int fd, int nstype)
-kcmp 272 378 (pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2)
-openat 56 322 (int dirfd, const char *pathname, int flags, mode_t mode)
-mkdirat 34 323 (int dirfd, const char *pathname, mode_t mode)
-unlinkat 35 328 (int dirfd, const char *pathname, int flags)
-memfd_create 279 385 (const char *name, unsigned int flags)
-io_setup 0 243 (unsigned nr_events, aio_context_t *ctx)
-io_destroy 1 244 (aio_context_t ctx)
-io_submit 2 246 (aio_context_t ctx_id, long nr, struct iocb **iocbpp)
-io_getevents 4 245 (aio_context_t ctx, long min_nr, long nr, struct io_event *evs, struct timespec *tmo)
-seccomp 277 383 (unsigned int op, unsigned int flags, const char *uargs)
-gettimeofday 169 78 (struct timeval *tv, struct timezone *tz)
-preadv_raw 69 361 (int fd, struct iovec *iov, unsigned long nr, unsigned long pos_l, unsigned long pos_h)
-userfaultfd 282 388 (int flags)
-fallocate 47 352 (int fd, int mode, loff_t offset, loff_t len)
-cacheflush ! 983042 (void *start, void *end, int flags)
-ppoll 73 336 (struct pollfd *fds, unsigned int nfds, const struct timespec *tmo, const sigset_t *sigmask, size_t sigsetsize)
-open_tree 428 428 (int dirfd, const char *pathname, unsigned int flags)
-move_mount 429 429 (int from_dfd, const char *from_pathname, int to_dfd, const char *to_pathname, int flags)
-fsopen 430 430 (char *fsname, unsigned int flags)
-fsconfig 431 431 (int fd, unsigned int cmd, const char *key, const char *value, int aux)
-fsmount 432 432 (int fd, unsigned int flags, unsigned int attr_flags)
-clone3 435 435 (struct clone_args *uargs, size_t size)
-close_range 436 436 (unsigned int fd, unsigned int max_fd, unsigned int flags)
-pidfd_open 434 434 (pid_t pid, unsigned int flags)
-openat2 437 437 (int dirfd, char *pathname, struct open_how *how, size_t size)
-pidfd_getfd 438 438 (int pidfd, int targetfd, unsigned int flags)
-rseq 293 398 (void *rseq, uint32_t rseq_len, int flags, uint32_t sig)
-membarrier 283 389 (int cmd, unsigned int flags, int cpu_id)
-map_shadow_stack 453 ! (unsigned long addr, unsigned long size, unsigned int flags)
\ No newline at end of file
diff --git a/compel/arch/arm/scripts/compel-pack.lds.S b/compel/arch/arm/scripts/compel-pack.lds.S
deleted file mode 100644
index 3d97bb139..000000000
--- a/compel/arch/arm/scripts/compel-pack.lds.S
+++ /dev/null
@@ -1,32 +0,0 @@
-OUTPUT_ARCH(arm)
-EXTERN(__export_parasite_head_start)
-
-SECTIONS
-{
- .crblob 0x0 : {
- *(.head.text)
- ASSERT(DEFINED(__export_parasite_head_start),
- "Symbol __export_parasite_head_start is missing");
- *(.text*)
- . = ALIGN(32);
- *(.data*)
- . = ALIGN(32);
- *(.rodata*)
- . = ALIGN(32);
- *(.bss*)
- . = ALIGN(32);
- *(.got*)
- . = ALIGN(32);
- *(.toc*)
- . = ALIGN(32);
- } =0x00000000,
-
- /DISCARD/ : {
- *(.debug*)
- *(.comment*)
- *(.note*)
- *(.group*)
- *(.eh_frame*)
- *(*)
- }
-}
diff --git a/compel/arch/arm/src/lib/cpu.c b/compel/arch/arm/src/lib/cpu.c
deleted file mode 120000
index ceb924b73..000000000
--- a/compel/arch/arm/src/lib/cpu.c
+++ /dev/null
@@ -1 +0,0 @@
-../../../aarch64/src/lib/cpu.c
\ No newline at end of file
diff --git a/compel/arch/arm/src/lib/handle-elf-host.c b/compel/arch/arm/src/lib/handle-elf-host.c
deleted file mode 120000
index fe4611886..000000000
--- a/compel/arch/arm/src/lib/handle-elf-host.c
+++ /dev/null
@@ -1 +0,0 @@
-handle-elf.c
\ No newline at end of file
diff --git a/compel/arch/arm/src/lib/handle-elf.c b/compel/arch/arm/src/lib/handle-elf.c
deleted file mode 100644
index a84524abd..000000000
--- a/compel/arch/arm/src/lib/handle-elf.c
+++ /dev/null
@@ -1,20 +0,0 @@
-#include
-#include
-
-#include "handle-elf.h"
-#include "piegen.h"
-#include "log.h"
-
-static const unsigned char __maybe_unused elf_ident_32[EI_NIDENT] = {
- 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, /* clang-format */
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-int handle_binary(void *mem, size_t size)
-{
- if (memcmp(mem, elf_ident_32, sizeof(elf_ident_32)) == 0)
- return handle_elf_arm(mem, size);
-
- pr_err("Unsupported Elf format detected\n");
- return -EINVAL;
-}
diff --git a/compel/arch/arm/src/lib/include/cpu.h b/compel/arch/arm/src/lib/include/cpu.h
deleted file mode 100644
index e69de29bb..000000000
diff --git a/compel/arch/arm/src/lib/include/handle-elf.h b/compel/arch/arm/src/lib/include/handle-elf.h
deleted file mode 100644
index 4b5e1457a..000000000
--- a/compel/arch/arm/src/lib/include/handle-elf.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef COMPEL_HANDLE_ELF_H__
-#define COMPEL_HANDLE_ELF_H__
-
-#include "elf32-types.h"
-
-#define __handle_elf handle_elf_arm
-#define arch_is_machine_supported(e_machine) (e_machine == EM_ARM)
-
-extern int handle_elf_arm(void *mem, size_t size);
-
-#endif /* COMPEL_HANDLE_ELF_H__ */
diff --git a/compel/arch/arm/src/lib/include/syscall.h b/compel/arch/arm/src/lib/include/syscall.h
deleted file mode 100644
index 13ee906e1..000000000
--- a/compel/arch/arm/src/lib/include/syscall.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __COMPEL_SYSCALL_H__
-#define __COMPEL_SYSCALL_H__
-#define __NR(syscall, compat) \
- ({ \
- (void)compat; \
- __NR_##syscall; \
- })
-#endif
diff --git a/compel/arch/arm/src/lib/include/uapi/asm/.gitignore b/compel/arch/arm/src/lib/include/uapi/asm/.gitignore
deleted file mode 100644
index e69de29bb..000000000
diff --git a/compel/arch/arm/src/lib/include/uapi/asm/cpu.h b/compel/arch/arm/src/lib/include/uapi/asm/cpu.h
deleted file mode 100644
index 12e749508..000000000
--- a/compel/arch/arm/src/lib/include/uapi/asm/cpu.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef UAPI_COMPEL_ASM_CPU_H__
-#define UAPI_COMPEL_ASM_CPU_H__
-
-typedef struct {
-} compel_cpuinfo_t;
-
-#endif /* UAPI_COMPEL_ASM_CPU_H__ */
diff --git a/compel/arch/arm/src/lib/include/uapi/asm/infect-types.h b/compel/arch/arm/src/lib/include/uapi/asm/infect-types.h
deleted file mode 100644
index 5fd94ca7b..000000000
--- a/compel/arch/arm/src/lib/include/uapi/asm/infect-types.h
+++ /dev/null
@@ -1,76 +0,0 @@
-#ifndef UAPI_COMPEL_ASM_TYPES_H__
-#define UAPI_COMPEL_ASM_TYPES_H__
-
-#include
-#include
-#include
-
-#define SIGMAX 64
-#define SIGMAX_OLD 31
-
-/*
- * Copied from the Linux kernel header arch/arm/include/asm/ptrace.h
- *
- * A thread ARM CPU context
- */
-
-typedef struct {
- long uregs[18];
-} user_regs_struct_t;
-
-#define __compel_arch_fetch_thread_area(tid, th) 0
-#define compel_arch_fetch_thread_area(tctl) 0
-#define compel_arch_get_tls_task(ctl, tls)
-#define compel_arch_get_tls_thread(tctl, tls)
-
-typedef struct user_vfp user_fpregs_struct_t;
-
-#define ARM_cpsr uregs[16]
-#define ARM_pc uregs[15]
-#define ARM_lr uregs[14]
-#define ARM_sp uregs[13]
-#define ARM_ip uregs[12]
-#define ARM_fp uregs[11]
-#define ARM_r10 uregs[10]
-#define ARM_r9 uregs[9]
-#define ARM_r8 uregs[8]
-#define ARM_r7 uregs[7]
-#define ARM_r6 uregs[6]
-#define ARM_r5 uregs[5]
-#define ARM_r4 uregs[4]
-#define ARM_r3 uregs[3]
-#define ARM_r2 uregs[2]
-#define ARM_r1 uregs[1]
-#define ARM_r0 uregs[0]
-#define ARM_ORIG_r0 uregs[17]
-
-/* Copied from arch/arm/include/asm/user.h */
-
-struct user_vfp {
- unsigned long long fpregs[32];
- unsigned long fpscr;
-};
-
-struct user_vfp_exc {
- unsigned long fpexc;
- unsigned long fpinst;
- unsigned long fpinst2;
-};
-
-#define REG_RES(regs) ((regs).ARM_r0)
-#define REG_IP(regs) ((regs).ARM_pc)
-#define SET_REG_IP(regs, val) ((regs).ARM_pc = (val))
-#define REG_SP(regs) ((regs).ARM_sp)
-#define REG_SYSCALL_NR(regs) ((regs).ARM_r7)
-
-#define user_regs_native(pregs) true
-
-#define ARCH_SI_TRAP TRAP_BRKPT
-
-#define __NR(syscall, compat) \
- ({ \
- (void)compat; \
- __NR_##syscall; \
- })
-
-#endif /* UAPI_COMPEL_ASM_TYPES_H__ */
diff --git a/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h b/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h
deleted file mode 100644
index 36edf231a..000000000
--- a/compel/arch/arm/src/lib/include/uapi/asm/processor-flags.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef __CR_PROCESSOR_FLAGS_H__
-#define __CR_PROCESSOR_FLAGS_H__
-
-/* Copied from the Linux kernel header arch/arm/include/uapi/asm/ptrace.h */
-
-/*
- * PSR bits
- */
-#define USR26_MODE 0x00000000
-#define FIQ26_MODE 0x00000001
-#define IRQ26_MODE 0x00000002
-#define SVC26_MODE 0x00000003
-#define USR_MODE 0x00000010
-#define FIQ_MODE 0x00000011
-#define IRQ_MODE 0x00000012
-#define SVC_MODE 0x00000013
-#define ABT_MODE 0x00000017
-#define UND_MODE 0x0000001b
-#define SYSTEM_MODE 0x0000001f
-#define MODE32_BIT 0x00000010
-#define MODE_MASK 0x0000001f
-#define PSR_T_BIT 0x00000020
-#define PSR_F_BIT 0x00000040
-#define PSR_I_BIT 0x00000080
-#define PSR_A_BIT 0x00000100
-#define PSR_E_BIT 0x00000200
-#define PSR_J_BIT 0x01000000
-#define PSR_Q_BIT 0x08000000
-#define PSR_V_BIT 0x10000000
-#define PSR_C_BIT 0x20000000
-#define PSR_Z_BIT 0x40000000
-#define PSR_N_BIT 0x80000000
-
-/*
- * Groups of PSR bits
- */
-#define PSR_f 0xff000000 /* Flags */
-#define PSR_s 0x00ff0000 /* Status */
-#define PSR_x 0x0000ff00 /* Extension */
-#define PSR_c 0x000000ff /* Control */
-
-#endif
diff --git a/compel/arch/arm/src/lib/include/uapi/asm/sigframe.h b/compel/arch/arm/src/lib/include/uapi/asm/sigframe.h
deleted file mode 100644
index 3db9978d0..000000000
--- a/compel/arch/arm/src/lib/include/uapi/asm/sigframe.h
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef UAPI_COMPEL_ASM_SIGFRAME_H__
-#define UAPI_COMPEL_ASM_SIGFRAME_H__
-
-#include
-
-/* Copied from the Linux kernel header arch/arm/include/asm/sigcontext.h */
-
-struct rt_sigcontext {
- unsigned long trap_no;
- unsigned long error_code;
- unsigned long oldmask;
- unsigned long arm_r0;
- unsigned long arm_r1;
- unsigned long arm_r2;
- unsigned long arm_r3;
- unsigned long arm_r4;
- unsigned long arm_r5;
- unsigned long arm_r6;
- unsigned long arm_r7;
- unsigned long arm_r8;
- unsigned long arm_r9;
- unsigned long arm_r10;
- unsigned long arm_fp;
- unsigned long arm_ip;
- unsigned long arm_sp;
- unsigned long arm_lr;
- unsigned long arm_pc;
- unsigned long arm_cpsr;
- unsigned long fault_address;
-};
-
-/* Copied from the Linux kernel header arch/arm/include/asm/ucontext.h */
-
-#define VFP_MAGIC 0x56465001
-#define VFP_STORAGE_SIZE sizeof(struct vfp_sigframe)
-
-struct vfp_sigframe {
- unsigned long magic;
- unsigned long size;
- struct user_vfp ufp;
- struct user_vfp_exc ufp_exc;
-};
-
-typedef struct vfp_sigframe fpu_state_t;
-
-struct aux_sigframe {
- /*
- struct crunch_sigframe crunch;
- struct iwmmxt_sigframe iwmmxt;
- */
-
- struct vfp_sigframe vfp;
- unsigned long end_magic;
-} __attribute__((aligned(8)));
-
-#include
-
-struct sigframe {
- struct rt_ucontext uc;
- unsigned long retcode[2];
-};
-
-struct rt_sigframe {
- struct rt_siginfo info;
- struct sigframe sig;
-};
-
-/* clang-format off */
-#define ARCH_RT_SIGRETURN(new_sp, rt_sigframe) \
- asm volatile( \
- "mov sp, %0 \n" \
- "mov r7, #"__stringify(__NR_rt_sigreturn)" \n" \
- "svc #0 \n" \
- : \
- : "r"(new_sp) \
- : "memory")
-/* clang-format on */
-
-#define RT_SIGFRAME_UC(rt_sigframe) (&rt_sigframe->sig.uc)
-#define RT_SIGFRAME_REGIP(rt_sigframe) (rt_sigframe)->sig.uc.uc_mcontext.arm_ip
-#define RT_SIGFRAME_HAS_FPU(rt_sigframe) 1
-#define RT_SIGFRAME_AUX_SIGFRAME(rt_sigframe) ((struct aux_sigframe *)&(rt_sigframe)->sig.uc.uc_regspace)
-#define RT_SIGFRAME_FPU(rt_sigframe) (&RT_SIGFRAME_AUX_SIGFRAME(rt_sigframe)->vfp)
-#define RT_SIGFRAME_OFFSET(rt_sigframe) 0
-
-#define rt_sigframe_erase_sigset(sigframe) memset(&sigframe->sig.uc.uc_sigmask, 0, sizeof(k_rtsigset_t))
-#define rt_sigframe_copy_sigset(sigframe, from) memcpy(&sigframe->sig.uc.uc_sigmask, from, sizeof(k_rtsigset_t))
-
-#endif /* UAPI_COMPEL_ASM_SIGFRAME_H__ */
diff --git a/compel/arch/arm/src/lib/infect.c b/compel/arch/arm/src/lib/infect.c
deleted file mode 100644
index a9fb639e2..000000000
--- a/compel/arch/arm/src/lib/infect.c
+++ /dev/null
@@ -1,193 +0,0 @@
-#include
-#include