* feat: Update rbpf to mainline Enable JIT compilation on x86 platform. Signed-off-by: Godones <chenlinfeng25@outlook.com> * fix: remove rbpf test Signed-off-by: Godones <chenlinfeng25@outlook.com> * fix: remove rbpf info Signed-off-by: Godones <chenlinfeng25@outlook.com> --------- Signed-off-by: Godones <chenlinfeng25@outlook.com>
@@ -51,7 +51,7 @@ jobs:
env:
ARCH: ${{ matrix.arch }}
HOME: /root
- run: bash -c "source /root/.cargo/env && cd kernel && make test && make test-rbpf"
+ run: bash -c "source /root/.cargo/env && cd kernel && make test"
build:
name: Build ${{ matrix.arch }}
@@ -39,18 +39,6 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
-[[package]]
-name = "anyhow"
-version = "1.0.97"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
-
-name = "arbitrary"
-version = "1.4.1"
-checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223"
[[package]]
name = "asm_macros"
version = "0.1.0"
@@ -83,12 +71,6 @@ version = "1.4.0"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
-name = "base-x"
-version = "0.2.11"
-checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
name = "bindgen"
version = "0.61.0"
@@ -144,30 +126,12 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
name = "bitmap"
-name = "bumpalo"
-version = "3.17.0"
-checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
-name = "byteorder"
-version = "0.5.3"
-checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855"
name = "byteorder"
version = "1.5.0"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-name = "bytes"
-version = "1.10.1"
-checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
name = "cc"
version = "1.2.17"
@@ -221,136 +185,9 @@ version = "4.6.7"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [
- "bytes",
"memchr",
]
-name = "const_fn"
-version = "0.4.11"
-checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e"
-name = "cranelift-bforest"
-version = "0.99.2"
-checksum = "5a91a1ccf6fb772808742db2f51e2179f25b1ec559cbe39ea080c72ff61caf8f"
-dependencies = [
- "cranelift-entity",
-]
-name = "cranelift-codegen"
-checksum = "169db1a457791bff4fd1fc585bb5cc515609647e0420a7d5c98d7700c59c2d00"
- "bumpalo",
- "cranelift-bforest",
- "cranelift-codegen-meta",
- "cranelift-codegen-shared",
- "cranelift-control",
- "cranelift-isle",
- "gimli 0.27.3",
- "hashbrown 0.13.2",
- "log",
- "regalloc2",
- "smallvec",
- "target-lexicon",
-name = "cranelift-codegen-meta"
-checksum = "3486b93751ef19e6d6eef66d2c0e83ed3d2ba01da1919ed2747f2f7bd8ba3419"
-name = "cranelift-codegen-shared"
-checksum = "86a1205ab18e7cd25dc4eca5246e56b506ced3feb8d95a8d776195e48d2cd4ef"
-name = "cranelift-control"
-checksum = "1b108cae0f724ddfdec1871a0dc193a607e0c2d960f083cfefaae8ccf655eff2"
- "arbitrary",
-name = "cranelift-entity"
-checksum = "720444006240622798665bfc6aa8178e2eed556da342fda62f659c5267c3c659"
-name = "cranelift-frontend"
-checksum = "b7a94c4c5508b7407e125af9d5320694b7423322e59a4ac0d07919ae254347ca"
- "cranelift-codegen",
-name = "cranelift-isle"
-checksum = "ef1f888d0845dcd6be4d625b91d9d8308f3d95bed5c5d4072ce38e1917faa505"
-name = "cranelift-jit"
-checksum = "547845cd12d15167e5458556ee67513bfaff2e05e72eb489edfbabc9f21d9ea2"
- "anyhow",
- "cranelift-module",
- "cranelift-native",
- "libc",
- "region",
- "wasmtime-jit-icache-coherence",
- "windows-sys 0.48.0",
-name = "cranelift-module"
-checksum = "3c104ed6a4c56c15e1858cc466482373e3c13d022bc1391485769ba384d9b079"
-name = "cranelift-native"
-checksum = "9ad5966da08f1e96a3ae63be49966a85c9b249fa465f8cf1b66469a82b1004a0"
name = "crc"
@@ -476,12 +313,6 @@ dependencies = [
"syn 2.0.100",
-name = "discard"
-version = "1.0.4"
-checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
name = "doc-comment"
version = "0.3.3"
@@ -503,7 +334,7 @@ dependencies = [
"defer",
"derive_builder",
"driver_base_macros",
- "elf 0.7.2",
+ "elf",
"fdt",
"hashbrown 0.13.2",
"ida",
@@ -553,15 +384,6 @@ version = "1.15.0"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
-name = "elf"
-version = "0.0.10"
-checksum = "4841de15dbe0e49b9b62a417589299e3be0d557e0900d36acb87e6dae47197f5"
- "byteorder 0.5.3",
name = "elf"
version = "0.7.2"
@@ -600,15 +422,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
"libc",
- "windows-sys 0.59.0",
+ "windows-sys",
-name = "fallible-iterator"
-version = "0.2.0"
-checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
name = "fdt"
version = "0.2.0-alpha1"
@@ -637,17 +453,6 @@ dependencies = [
"wasi",
-name = "gimli"
-version = "0.27.3"
-checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
- "fallible-iterator",
- "indexmap 1.9.3",
- "stable_deref_trait",
name = "gimli"
version = "0.31.1"
@@ -666,15 +471,9 @@ version = "0.3.1"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
- "byteorder 1.5.0",
+ "byteorder",
-name = "hashbrown"
-version = "0.12.3"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
name = "hashbrown"
version = "0.13.2"
@@ -714,19 +513,13 @@ dependencies = [
-name = "hex"
-version = "0.4.3"
-checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
name = "home"
version = "0.5.11"
checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
@@ -748,16 +541,6 @@ version = "1.0.1"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
-name = "indexmap"
-version = "1.9.3"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
- "autocfg",
- "hashbrown 0.12.3",
name = "indexmap"
version = "2.8.0"
@@ -817,12 +600,6 @@ dependencies = [
-name = "json"
-version = "0.11.15"
-checksum = "92c245af8786f6ac35f95ca14feca9119e71339aaab41e878e7cdd655c97e9e5"
name = "kcmdline_macros"
@@ -956,15 +733,6 @@ dependencies = [
"hashbrown 0.15.2",
-name = "mach"
-version = "0.3.2"
-checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
name = "mach2"
version = "0.4.2"
@@ -1192,12 +960,6 @@ dependencies = [
-name = "proc-macro-hack"
-version = "0.5.20+deprecated"
-checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
name = "proc-macro2"
version = "1.0.94"
@@ -1286,34 +1048,13 @@ dependencies = [
name = "rbpf"
+version = "0.3.0"
+source = "git+https://git.mirrors.dragonos.org.cn/DragonOS-Community/rbpf?rev=f31e471a29#f31e471a29dea9a0c272626f7b762adba755e6cc"
"combine",
- "cranelift-frontend",
- "cranelift-jit",
- "elf 0.0.10",
- "hex",
- "json",
- "time",
-name = "regalloc2"
-version = "0.9.3"
-checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6"
+ "hashbrown 0.15.2",
"log",
- "rustc-hash",
- "slice-group-by",
@@ -1345,18 +1086,6 @@ version = "0.8.5"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
-name = "region"
-version = "2.2.0"
-checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0"
- "bitflags 1.3.2",
- "mach",
- "winapi",
name = "ringbuffer"
version = "0.15.0"
@@ -1378,15 +1107,6 @@ version = "1.1.0"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-name = "rustc_version"
-version = "0.2.3"
-checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
- "semver",
name = "rustix"
version = "0.38.44"
@@ -1397,7 +1117,7 @@ dependencies = [
"errno",
"linux-raw-sys",
@@ -1433,21 +1153,6 @@ version = "1.2.0"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-name = "semver"
-version = "0.9.0"
-checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
- "semver-parser",
-name = "semver-parser"
-version = "0.7.0"
-checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
name = "serde"
version = "1.0.219"
@@ -1489,21 +1194,6 @@ dependencies = [
"serde",
-name = "sha1"
-version = "0.6.1"
-checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
- "sha1_smol",
-name = "sha1_smol"
-version = "1.0.1"
-checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
name = "shlex"
version = "1.3.0"
@@ -1520,12 +1210,6 @@ dependencies = [
"spin 0.9.8",
-name = "slice-group-by"
-version = "0.3.1"
-checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7"
name = "smallvec"
version = "1.14.0"
@@ -1539,7 +1223,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97"
"bitflags 1.3.2",
"cfg-if",
"defmt",
"heapless",
@@ -1567,15 +1251,6 @@ version = "1.2.0"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
-name = "standback"
-version = "0.2.17"
-checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff"
- "version_check",
name = "static-keys"
version = "0.7.0"
@@ -1588,55 +1263,6 @@ dependencies = [
"windows 0.59.0",
-name = "stdweb"
-version = "0.4.20"
-checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
- "discard",
- "rustc_version",
- "stdweb-derive",
- "stdweb-internal-macros",
- "stdweb-internal-runtime",
- "wasm-bindgen",
-name = "stdweb-derive"
-checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
- "proc-macro2",
- "quote",
- "serde",
- "serde_derive",
- "syn 1.0.109",
-name = "stdweb-internal-macros"
-version = "0.2.9"
-checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
- "base-x",
- "serde_json",
- "sha1",
-name = "stdweb-internal-runtime"
-version = "0.1.5"
-checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
name = "strsim"
version = "0.11.1"
@@ -1677,12 +1303,6 @@ dependencies = [
"num-traits 0.2.15",
-name = "target-lexicon"
-version = "0.12.16"
-checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
name = "target-triple"
version = "0.1.4"
@@ -1727,44 +1347,6 @@ dependencies = [
-name = "time"
-version = "0.2.27"
-checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242"
- "const_fn",
- "standback",
- "stdweb",
- "time-macros",
-name = "time-macros"
-version = "0.1.1"
-checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1"
- "proc-macro-hack",
- "time-macros-impl",
-name = "time-macros-impl"
-version = "0.1.2"
-checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f"
name = "toml"
version = "0.8.20"
@@ -1792,7 +1374,7 @@ version = "0.22.24"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
- "indexmap 2.8.0",
+ "indexmap",
"serde_spanned",
"toml_datetime",
@@ -1900,7 +1482,7 @@ version = "0.2.3"
checksum = "637d511437df708cee34bdec7ba2f1548d256b7acf3ff20e0a1c559f9bf3a987"
- "gimli 0.31.1",
+ "gimli",
@@ -1944,75 +1526,6 @@ version = "0.11.0+wasi-snapshot-preview1"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-name = "wasm-bindgen"
-version = "0.2.100"
-checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
- "cfg-if",
- "once_cell",
- "rustversion",
- "wasm-bindgen-macro",
-name = "wasm-bindgen-backend"
-checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
- "syn 2.0.100",
- "wasm-bindgen-shared",
-name = "wasm-bindgen-macro"
-checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
- "wasm-bindgen-macro-support",
-name = "wasm-bindgen-macro-support"
-checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
- "wasm-bindgen-backend",
-name = "wasm-bindgen-shared"
-checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
- "unicode-ident",
-name = "wasmtime-jit-icache-coherence"
-version = "12.0.2"
-checksum = "b59f94b0409221873565419168e20b5aedf18c4bd64de5c38acf8f0634efeee3"
name = "which"
version = "4.4.2"
@@ -2047,7 +1560,7 @@ version = "0.1.9"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
@@ -2189,15 +1702,6 @@ dependencies = [
"windows-link",
-name = "windows-sys"
-version = "0.48.0"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
- "windows-targets 0.48.5",
name = "windows-sys"
version = "0.59.0"
@@ -2207,21 +1711,6 @@ dependencies = [
"windows-targets 0.52.6",
-name = "windows-targets"
-version = "0.48.5"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
- "windows_aarch64_gnullvm 0.48.5",
- "windows_aarch64_msvc 0.48.5",
- "windows_i686_gnu 0.48.5",
- "windows_i686_msvc 0.48.5",
- "windows_x86_64_gnu 0.48.5",
- "windows_x86_64_gnullvm 0.48.5",
- "windows_x86_64_msvc 0.48.5",
name = "windows-targets"
version = "0.52.6"
@@ -2254,12 +1743,6 @@ dependencies = [
"windows_x86_64_msvc 0.53.0",
-name = "windows_aarch64_gnullvm"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
name = "windows_aarch64_gnullvm"
@@ -2272,12 +1755,6 @@ version = "0.53.0"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
-name = "windows_aarch64_msvc"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
name = "windows_aarch64_msvc"
@@ -2290,12 +1767,6 @@ version = "0.53.0"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
-name = "windows_i686_gnu"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
name = "windows_i686_gnu"
@@ -2320,12 +1791,6 @@ version = "0.53.0"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
-name = "windows_i686_msvc"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
name = "windows_i686_msvc"
@@ -2338,12 +1803,6 @@ version = "0.53.0"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
-name = "windows_x86_64_gnu"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
name = "windows_x86_64_gnu"
@@ -2356,12 +1815,6 @@ version = "0.53.0"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
-name = "windows_x86_64_gnullvm"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
name = "windows_x86_64_gnullvm"
@@ -2374,12 +1827,6 @@ version = "0.53.0"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
-name = "windows_x86_64_msvc"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
name = "windows_x86_64_msvc"
@@ -2458,7 +1905,7 @@ version = "0.7.35"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
"zerocopy-derive 0.7.35",
@@ -75,7 +75,7 @@ log = "0.4.21"
kprobe = { path = "crates/kprobe" }
lru = "0.12.3"
-rbpf = { path = "crates/rbpf" }
+rbpf = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/rbpf", rev = "f31e471a29", default-features = false }
printf-compat = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/printf-compat", rev = "5f5c9cc363", default-features = false }
static-keys = { version = "=0.7" }
@@ -41,7 +41,4 @@ check: ECHO
test:
# 测试内核库
- RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-11-05 test --workspace --exclude dragonos_kernel rbpf
-test-rbpf:
- cd crates/rbpf && RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-11-05 test --features=std,user,cranelift
+ RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-11-05 test --workspace --exclude dragonos_kernel
@@ -1,21 +0,0 @@
-version: 1.0.{build}
-branches:
- only:
- - main
-os:
- - Visual Studio 2015
-clone_depth: 1
-configuration:
- - Debug
-platform:
- - x64
-environment:
- matrix:
- - TOOLCHAIN_VERSION: 14.0
- RUST: 1.76.0
- RUST: beta
- RUST: nightly
-build_script: mk/appveyor.bat
@@ -1,2 +0,0 @@
-target
-Cargo.lock
@@ -1,78 +0,0 @@
-[package]
-# Project metadata
-name = "rbpf"
-authors = ["Quentin <quentin@isovalent.com>"]
-# Additional metadata for packaging
-description = "Virtual machine and JIT compiler for eBPF programs"
-repository = "https://github.com/qmonnet/rbpf"
-readme = "README.md"
-keywords = ["BPF", "eBPF", "interpreter", "JIT", "filtering"]
-license = "Apache-2.0/MIT"
-edition = "2021"
-# Packaging directives
-include = [
- "src/**",
- "examples/**",
- "tests/**",
- "bench/**",
- "LICENSE*",
- "Cargo.toml",
-[dependencies]
-# Default features (std) are disabled so that the dependencies don't pull in the
-# standard library when the crate is compiled for no_std
-byteorder = { version = "1.2", default-features = false }
-log = {version = "0.4.21", default-features = false }
-combine = { version = "4.6", default-features = false }
-# Optional Dependencies when using the standard library
-libc = { version = "0.2", optional = true }
-time = { version = "0.2", optional = true }
-# Optional Dependencies for the CraneLift JIT
-cranelift-codegen = { version = "0.99", optional = true }
-cranelift-frontend = { version = "0.99", optional = true }
-cranelift-jit = { version = "0.99", optional = true }
-cranelift-native = { version = "0.99", optional = true }
-cranelift-module = { version = "0.99", optional = true }
-[dev-dependencies]
-elf = "0.0.10"
-json = "0.11"
-hex = "0.4.3"
-[features]
-#default = ["std", "user", "cranelift"]
-cargo-clippy = []
-std = ["dep:time", "dep:libc", "combine/std"]
-cranelift = [
- "dep:cranelift-codegen",
- "dep:cranelift-frontend",
- "dep:cranelift-jit",
- "dep:cranelift-native",
- "dep:cranelift-module",
-user = []
-# Examples that depend on the standard library should be disabled when
-# testing the `no_std` configuration.
-[[example]]
-name = "disassemble"
-required-features = ["std"]
-name = "uptime"
-name = "to_json"
-name = "rbpf_plugin"
@@ -1,202 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
- 1. Definitions.
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
- 3. Grant of Patent License. Subject to the terms and conditions of
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
- END OF TERMS AND CONDITIONS
- APPENDIX: How to apply the Apache License to your work.
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
- Copyright [yyyy] [name of copyright owner]
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
@@ -1,25 +0,0 @@
-Copyright (c) 2016 6WIND S.A.
-Permission is hereby granted, free of charge, to any
-person obtaining a copy of this software and associated
-documentation files (the "Software"), to deal in the
-Software without restriction, including without
-limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software
-is furnished to do so, subject to the following
-conditions:
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions
-of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
-ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
-IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
@@ -1,743 +0,0 @@
-# rbpf
-<picture>
- <source media="(prefers-color-scheme: dark)" srcset="misc/rbpf_256_border.png">
- <img src="misc/rbpf_256.png">
-</picture>
-Rust (user-space) virtual machine for eBPF
-[](https://github.com/qmonnet/rbpf/actions/workflows/test.yaml)
-[](https://ci.appveyor.com/project/qmonnet/rbpf/branch/main)
-[](https://coveralls.io/github/qmonnet/rbpf?branch=main)
-[](https://crates.io/crates/rbpf)
-* [Description](#description)
-* [Link to the crate](#link-to-the-crate)
-* [API](#api)
-* [Example uses](#example-uses)
-* [Building eBPF programs](#building-ebpf-programs)
-* [Build Features](#build-features)
-* [Feedback welcome!](#feedback-welcome)
-* [Questions / Answers](#questions--answers)
-* [Caveats](#caveats)
-* [_To do_ list](#to-do-list)
-* [License](#license)
-* [Inspired by](#inspired-by)
-* [Other resources](#other-resources)
-## Description
-This crate contains a virtual machine for eBPF program execution. BPF, as in
-_Berkeley Packet Filter_, is an assembly-like language initially developed for
-BSD systems, in order to filter packets in the kernel with tools such as
-tcpdump so as to avoid useless copies to user-space. It was ported to Linux,
-where it evolved into eBPF (_extended_ BPF), a faster version with more
-features. While BPF programs are originally intended to run in the kernel, the
-virtual machine of this crate enables running it in user-space applications;
-it contains an interpreter, an x86_64 JIT-compiler for eBPF programs, as well as
-a disassembler.
-It is based on Rich Lane's [uBPF software](https://github.com/iovisor/ubpf/),
-which does nearly the same, but is written in C.
-The crate is supposed to compile and run on Linux, MacOS X, and Windows,
-although the JIT-compiler does not work with Windows at this time.
-## Link to the crate
-This crate is available from [crates.io](https://crates.io/crates/rbpf), so it
-should work out of the box by adding it as a dependency in your `Cargo.toml`
-file:
-```toml
-rbpf = "0.2.0"
-```
-You can also use the development version from this GitHub repository. This
-should be as simple as putting this inside your `Cargo.toml`:
-rbpf = { git = "https://github.com/qmonnet/rbpf" }
-Of course, if you prefer, you can clone it locally, possibly hack the crate,
-and then indicate the path of your local version in `Cargo.toml`:
-rbpf = { path = "path/to/rbpf" }
-Then indicate in your source code that you want to use the crate:
-```rust,ignore
-extern crate rbpf;
-## API
-The API is pretty well documented inside the source code. You should also be
-able to access [an online version of the documentation from
-here](https://docs.rs/rbpf/), automatically generated from the
-[crates.io](https://crates.io/crates/rbpf) version (may not be up-to-date with
-the main branch). [Examples](../../tree/main/examples) and [unit
-tests](../../tree/main/tests) should also prove helpful. Here is a summary of
-how to use the crate.
-Here are the steps to follow to run an eBPF program with rbpf:
-1. Create a virtual machine. There are several kinds of machines, we will come
- back on this later. When creating the VM, pass the eBPF program as an
- argument to the constructor.
-2. If you want to use some helper functions, register them into the virtual
- machine.
-3. If you want a JIT-compiled program, compile it.
-4. Execute your program: either run the interpreter or call the JIT-compiled
- function.
-eBPF has been initially designed to filter packets (now it has some other hooks
-in the Linux kernel, such as kprobes, but this is not covered by rbpf). As a
-consequence, most of the load and store instructions of the program are
-performed on a memory area representing the packet data. However, in the Linux
-kernel, the eBPF program does not immediately access this data area: initially,
-it has access to a C `struct sk_buff` instead, which is a buffer containing
-metadata about the packet—including memory addresses of the beginning and of
-the end of the packet data area. So the program first loads those pointers from
-the `sk_buff`, and then can access the packet data.
-This behavior can be replicated with rbpf, but it is not mandatory. For this
-reason, we have several structs representing different kinds of virtual
-machines:
-* `struct EbpfVmMbuffer` mimics the kernel. When the program is run, the
- address provided to its first eBPF register will be the address of a metadata
- buffer provided by the user, and that is expected to contain pointers to the
- start and the end of the packet data memory area.
-* `struct EbpfVmFixedMbuff` has one purpose: enabling the execution of programs
- created to be compatible with the kernel, while saving the effort to manually
- handle the metadata buffer for the user. In fact, this struct has a static
- internal buffer that is passed to the program. The user has to indicate the
- offset values at which the eBPF program expects to find the start and the end
- of packet data in the buffer. On calling the function that runs the program
- (JITted or not), the struct automatically updates the addresses in this
- static buffer, at the appointed offsets, for the start and the end of the
- packet data the program is called upon.
-* `struct EbpfVmRaw` is for programs that want to run directly on packet data.
- No metadata buffer is involved, the eBPF program directly receives the
- address of the packet data in its first register. This is the behavior of
- uBPF.
-* `struct EbpfVmNoData` does not take any data. The eBPF program takes no
- argument whatsoever and its return value is deterministic. Not so sure there
- is a valid use case for that, but if nothing else, this is very useful for
- unit tests.
-All these structs implement the same public functions:
-// called with EbpfVmMbuff:: prefix
-pub fn new(prog: &'a [u8]) -> Result<EbpfVmMbuff<'a>, Error>
-// called with EbpfVmFixedMbuff:: prefix
-pub fn new(prog: &'a [u8],
- data_offset: usize,
- data_end_offset: usize) -> Result<EbpfVmFixedMbuff<'a>, Error>
-// called with EbpfVmRaw:: prefix
-pub fn new(prog: &'a [u8]) -> Result<EbpfVmRaw<'a>, Error>
-// called with EbpfVmNoData:: prefix
-pub fn new(prog: &'a [u8]) -> Result<EbpfVmNoData<'a>, Error>
-This is used to create a new instance of a VM. The return type is dependent of
-the struct from which the function is called. For instance,
-`rbpf::EbpfVmRaw::new(Some(my_program))` would return an instance of `struct
-rbpf::EbpfVmRaw` (wrapped in a `Result`). When a program is loaded, it is
-checked with a very simple verifier (nothing close to the one for Linux
-kernel). Users are also able to replace it with a custom verifier.
-For `struct EbpfVmFixedMbuff`, two additional arguments must be passed to the
-constructor: `data_offset` and `data_end_offset`. They are the offset (byte
-number) at which the pointers to the beginning and to the end, respectively, of
-the memory area of packet data are to be stored in the internal metadata buffer
-each time the program is executed. Other structs do not use this mechanism and
-do not need those offsets.
-// for struct EbpfVmMbuff, struct EbpfVmRaw and struct EbpfVmRawData
-pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error>
-// for struct EbpfVmFixedMbuff
-pub fn set_program(&mut self, prog: &'a [u8],
- data_end_offset: usize) -> Result<(), Error>
-You can use for example `my_vm.set_program(my_program);` to change the loaded
-program after the VM instance creation. This program is checked with the
-verifier attached to the VM. The verifying function of the VM can be changed at
-any moment.
-pub type Verifier = fn(prog: &[u8]) -> Result<(), Error>;
-pub fn set_verifier(&mut self,
- verifier: Verifier) -> Result<(), Error>
-Note that if a program has already been loaded into the VM, setting a new
-verifier also immediately runs it on the loaded program. However, the verifier
-is not run if no program has been loaded (if `None` was passed to the `new()`
-method when creating the VM).
-pub type Helper = fn (u64, u64, u64, u64, u64) -> u64;
-pub fn register_helper(&mut self,
- key: u32,
- function: Helper) -> Result<(), Error>
-This function is used to register a helper function. The VM stores its
-registers in a hashmap, so the key can be any `u32` value you want. It may be
-useful for programs that should be compatible with the Linux kernel and
-therefore must use specific helper numbers.
-// for struct EbpfVmMbuff
-pub fn execute_program(&self,
- mem: &'a mut [u8],
- mbuff: &'a mut [u8]) -> Result<(u64), Error>
-// for struct EbpfVmFixedMbuff and struct EbpfVmRaw
- mem: &'a mut [u8]) -> Result<(u64), Error>
-// for struct EbpfVmNoData
-pub fn execute_program(&self) -> Result<(u64), Error>
-Interprets the loaded program. The function takes a reference to the packet
-data and the metadata buffer, or only to the packet data, or nothing at all,
-depending on the kind of the VM used. The value returned is the result of the
-eBPF program.
-pub fn jit_compile(&mut self) -> Result<(), Error>
-JIT-compile the loaded program, for x86_64 architecture. If the program is to
-use helper functions, they must be registered into the VM before this function
-is called. The generated assembly function is internally stored in the VM.
-pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8],
-pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<(u64), Error>
-pub unsafe fn execute_program_jit(&self) -> Result<(u64), Error>
-Calls the JIT-compiled program. The arguments to provide are the same as for
-`execute_program()`, again depending on the kind of VM that is used. The result of
-the JIT-compiled program should be the same as with the interpreter, but it
-should run faster. Note that if errors occur during the program execution, the
-JIT-compiled version does not handle it as well as the interpreter, and the
-program may crash. For this reason, the functions are marked as `unsafe`.
-## Example uses
-### Simple example
-This comes from the unit test `test_vm_add`.
-```rust
-fn main() {
- // This is the eBPF program, in the form of bytecode instructions.
- let prog = &[
- 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov32 r0, 0
- 0xb4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov32 r1, 2
- 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // add32 r0, 1
- 0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // add32 r0, r1
- 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- ];
- // Instantiate a struct EbpfVmNoData. This is an eBPF VM for programs that
- // takes no packet data in argument.
- // The eBPF program is passed to the constructor.
- let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- // Execute (interpret) the program. No argument required for this VM.
- assert_eq!(vm.execute_program().unwrap(), 0x3);
-}
-### With JIT, on packet data
-This comes from the unit test `test_jit_ldxh`.
- 0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxh r0, [r1+2]
- // Let's use some data.
- let mem = &mut [
- 0xaa, 0xbb, 0x11, 0xcc, 0xdd
- // This is an eBPF VM for programs reading from a given memory area (it
- // directly reads from packet data)
- let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- #[cfg(any(windows, not(feature = "std")))] {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x11);
- }
- #[cfg(all(not(windows), feature = "std"))] {
- // This time we JIT-compile the program.
- vm.jit_compile().unwrap();
- // Then we execute it. For this kind of VM, a reference to the packet
- // data must be passed to the function that executes the program.
- unsafe { assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11); }
-### Using a metadata buffer
-This comes from the unit test `test_jit_mbuff` and derives from the unit test
-`test_jit_ldxh`.
- // Load mem from mbuff at offset 8 into R1
- 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
- // ldhx r1[2], r0
- 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
- 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- // Just for the example we create our metadata buffer from scratch, and
- // we store the pointers to packet data start and end in it.
- let mut mbuff = &mut [0u8; 32];
- unsafe {
- let mut data = mbuff.as_ptr().offset(8) as *mut u64;
- let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
- *data = mem.as_ptr() as u64;
- *data_end = mem.as_ptr() as u64 + mem.len() as u64;
- // This eBPF VM is for program that use a metadata buffer.
- let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- assert_eq!(vm.execute_program(mem, mbuff).unwrap(), 0x2211);
- // Here again we JIT-compile the program.
- // Here we must provide both a reference to the packet data, and to the
- // metadata buffer we use.
- assert_eq!(vm.execute_program_jit(mem, mbuff).unwrap(), 0x2211);
-### Loading code from an object file; and using a virtual metadata buffer
-This comes from unit test `test_vm_block_port`.
-This example requires the following additional crates, you may have to add them
-to your `Cargo.toml` file.
-It also uses a kind of VM that uses an internal buffer used to simulate the
-`sk_buff` used by eBPF programs in the kernel, without having to manually
-create a new buffer for each packet. It may be useful for programs compiled for
-the kernel and that assumes the data they receive is a `sk_buff` pointing to
-the packet data start and end addresses. So here we just provide the offsets at
-which the eBPF program expects to find those pointers, and the VM handles the
-buffer update so that we only have to provide a reference to the packet data
-for each run of the program.
-extern crate elf;
-use std::path::PathBuf;
-use rbpf::helpers;
- // Load a program from an ELF file, e.g. compiled from C to eBPF with
- // clang/LLVM. Some minor modification to the bytecode may be required.
- let filename = "examples/load_elf__block_a_port.elf";
- let path = PathBuf::from(filename);
- let file = match elf::File::open_path(&path) {
- Ok(f) => f,
- Err(e) => panic!("Error: {:?}", e),
- };
- // Here we assume the eBPF program is in the ELF section called
- // ".classifier".
- let text_scn = match file.get_section(".classifier") {
- Some(s) => s,
- None => panic!("Failed to look up .classifier section"),
- let prog = &text_scn.data;
- // This is our data: a real packet, starting with Ethernet header
- let packet = &mut [
- 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
- 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
- 0x08, 0x00, // ethertype
- 0x45, 0x00, 0x00, 0x3b, // start ip_hdr
- 0xa6, 0xab, 0x40, 0x00,
- 0x40, 0x06, 0x96, 0x0f,
- 0x7f, 0x00, 0x00, 0x01,
- 0x99, 0x99, 0xc6, 0xcc, // start tcp_hdr
- 0xd1, 0xe5, 0xc4, 0x9d,
- 0xd4, 0x30, 0xb5, 0xd2,
- 0x80, 0x18, 0x01, 0x56,
- 0xfe, 0x2f, 0x00, 0x00,
- 0x01, 0x01, 0x08, 0x0a, // start data
- 0x00, 0x23, 0x75, 0x89,
- 0x00, 0x23, 0x63, 0x2d,
- 0x71, 0x64, 0x66, 0x73,
- 0x64, 0x66, 0x0a
- // This is an eBPF VM for programs using a virtual metadata buffer, similar
- // to the sk_buff that eBPF programs use with tc and in Linux kernel.
- // We must provide the offsets at which the pointers to packet data start
- // and end must be stored: these are the offsets at which the program will
- // load the packet data from the metadata buffer.
- let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- // We register a helper function, that can be called by the program, into
- // the VM. The `bpf_trace_printf` is only available when we have access to
- // the standard library.
- #[cfg(feature = "std")] {
- vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX,
- helpers::bpf_trace_printf).unwrap();
- // This kind of VM takes a reference to the packet data, but does not need
- // any reference to the metadata buffer: a fixed buffer is handled
- // internally by the VM.
- let res = vm.execute_program(packet).unwrap();
- println!("Program returned: {:?} ({:#x})", res, res);
-## Building eBPF programs
-Besides passing the raw hexadecimal codes for building eBPF programs, two other
-methods are available.
-### Assembler
-The first method consists in using the assembler provided by the crate.
-use rbpf::assembler::assemble;
-let prog = assemble("add64 r1, 0x605
- mov64 r2, 0x32
- mov64 r1, r0
- be16 r0
- neg64 r2
- exit").unwrap();
-#[cfg(feature = "std")] {
- println!("{:?}", prog);
-The above snippet will produce:
-Ok([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
- 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
- 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
-Conversely, a disassembler is also available to dump instruction names from
-bytecode in a human-friendly format.
-use rbpf::disassembler::disassemble;
-let prog = &[
- 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
-];
-disassemble(prog);
-This will produce the following output:
-```txt
-add64 r1, 0x605
-mov64 r2, 0x32
-mov64 r1, r0
-be16 r0
-neg64 r2
-exit
-Please refer to [source code](src/assembler.rs) and [tests](tests/assembler.rs)
-for the syntax and the list of instruction names.
-### Building API
-The other way to build programs is to chain commands from the instruction
-builder API. It looks less like assembly, maybe more like high-level functions.
-What's sure is that the result is more verbose, but if you prefer to build
-programs this way, it works just as well. If we take again the same sample as
-above, it would be constructed as follows.
-use rbpf::insn_builder::*;
-let mut program = BpfCode::new();
-program.add(Source::Imm, Arch::X64).set_dst(1).set_imm(0x605).push()
- .mov(Source::Imm, Arch::X64).set_dst(2).set_imm(0x32).push()
- .mov(Source::Reg, Arch::X64).set_src(0).set_dst(1).push()
- .swap_bytes(Endian::Big).set_dst(0).set_imm(0x10).push()
- .negate(Arch::X64).set_dst(2).push()
- .exit().push();
-Again, please refer to [the source and related tests](src/insn_builder.rs) to
-get more information and examples on how to use it.
-## Build features
-### `no_std`
-The `rbpf` crate has a Cargo feature named "std" that is enabled by default. To
-use `rbpf` in `no_std` environments this feature needs to be disabled. To do
-this, you need to modify your dependency on `rbpf` in Cargo.toml to disable the
-enabled-by-default features.
-rbpf = { version = "1.0", default-features = false }
-Note that when using this crate in `no_std` environments, the `jit` module
-isn't available. This is because it depends on functions provided by `libc`
-(`libc::posix_memalign()`, `libc::mprotect()`) which aren't available on
-`no_std`.
-The `assembler` module is available, albeit with reduced debugging features. It
-depends on the `combine` crate providing parser combinators. Under `no_std`
-this crate only provides simple parsers which generate less descriptive error
-messages.
-## Feedback welcome!
-This is the author's first try at writing Rust code. He learned a lot in the
-process, but there remains a feeling that this crate has a kind of C-ish style
-in some places instead of the Rusty look the author would like it to have. So
-feedback (or PRs) are welcome, including about ways you might see to take
-better advantage of Rust features.
-Note that the project expects new commits to be covered by the
-[Developer's Certificate of Origin](https://wiki.linuxfoundation.org/dco).
-When contributing Pull Requests, please sign off your commits accordingly.
-## Questions / Answers
-### Why implementing an eBPF virtual machine in Rust?
-As of this writing, there is no particular use case for this crate at the best
-of the author's knowledge. The author happens to work with BPF on Linux and to
-know how uBPF works, and he wanted to learn and experiment with Rust—no more
-than that.
-### What are the differences with uBPF?
-Other than the language, obviously? Well, there are some differences:
-* Some constants, such as the maximum length for programs or the length for the
- stack, differs between uBPF and rbpf. The latter uses the same values as the
- Linux kernel, while uBPF has its own values.
-* When an error occurs while a program is run by uBPF, the function running the
- program silently returns the maximum value as an error code, while rbpf
- returns Rust type `Error`.
-* The registration of helper functions, that can be called from within an eBPF
- program, is not handled in the same way.
-* The distinct structs permitting to run program either on packet data, or with
- a metadata buffer (simulated or not) is a specificity of rbpf.
-* As for performance: theoretically the JITted programs are expected to run at
- the same speed, while the C interpreter of uBPF should go slightly faster
- than rbpf. But this has not been asserted yet. Benchmarking both programs
- would be an interesting thing to do.
-### Can I use it with the “classic” BPF (a.k.a cBPF) version?
-No. This crate only works with extended BPF (eBPF) programs. For cBPF programs,
-such as used by tcpdump (as of this writing) for example, you may be interested
-in the [bpfjit crate](https://crates.io/crates/bpfjit) written by Alexander
-Polakov instead.
-### What functionalities are implemented?
-Running and JIT-compiling eBPF programs work. There is also a mechanism to
-register user-defined helper functions. The eBPF implementation of the Linux
-kernel comes with [some additional
-features](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md):
-a high number of helpers, several kinds of maps, tail calls.
-* Additional helpers should be easy to add, but very few of the existing Linux
- helpers have been replicated in rbpf so far.
-* Tail calls (“long jumps” from an eBPF program into another) are not
- implemented. This is probably not trivial to design and implement.
-* The interaction with maps is done through the use of specific helpers, so
- this should not be difficult to add. The maps themselves can reuse the maps
- in the kernel (if on Linux), to communicate with in-kernel eBPF programs for
- instance; or they can be handled in user space. Rust has arrays and hashmaps,
- so their implementation should be pretty straightforward (and may be added to
- rbpf in the future).
-### What about program validation?
-The ”verifier” of this crate is very short and has nothing to do with the
-kernel verifier, which means that it accepts programs that may not be safe. On
-the other hand, you probably do not run this in a kernel here, so it will not
-crash your system. Implementing a verifier similar to the one in the kernel is
-not trivial, and we cannot “copy” it since it is under GPL license.
-### What about safety then?
-Rust has a strong emphasis on safety. Yet to have the eBPF VM work, some
-`unsafe` blocks of code are used. The VM, taken as an eBPF interpreter, can
-return an error but should not crash. Please file an issue otherwise.
-As for the JIT-compiler, it is a different story, since runtime memory checks
-are more complicated to implement in assembly. It _will_ crash if your
-JIT-compiled program tries to perform unauthorized memory accesses. Usually, it
-could be a good idea to test your program with the interpreter first.
-Oh, and if your program has infinite loops, even with the interpreter, you're
-on your own.
-## Caveats
-* This crate is **under development** and the API may be subject to change.
-* The JIT compiler produces an unsafe program: memory access are not tested at
- runtime (yet). Use with caution.
-* A small number of eBPF instructions have not been implemented yet. This
- should not be a problem for the majority of eBPF programs.
-* Beware of turnips. Turnips are disgusting.
-## _To do_ list
-* Implement some traits (`Clone`, `Drop`, `Debug` are good candidates).
-* Provide built-in support for user-space array and hash BPF maps.
-* Improve safety of JIT-compiled programs with runtime memory checks.
-* Add helpers (some of those supported in the kernel, such as checksum update,
- could be helpful).
-* Improve verifier. Could we find a way to directly support programs compiled
- with clang?
-* Maybe one day, tail calls?
-* JIT-compilers for other architectures?
-* …
-## License
-Following the effort of the Rust language project itself in order to ease
-integration with other projects, the rbpf crate is distributed under the terms
-of both the MIT license and the Apache License (Version 2.0).
-See
-[LICENSE-APACHE](https://github.com/qmonnet/rbpf/blob/main/LICENSE-APACHE)
-and [LICENSE-MIT](https://github.com/qmonnet/rbpf/blob/main/LICENSE-MIT) for
-details.
-## Version
-[The last commit](https://github.com/qmonnet/rbpf/commit/fe7021b07b08a43b836743a77796d07ce1f4902e)
-## Inspired by
-* [uBPF](https://github.com/iovisor/ubpf), a C user-space implementation of an
- eBPF virtual machine, with a JIT-compiler and disassembler (and also
- including the assembler from the human-readable form of the instructions,
- such as in `mov r0, 0x1337`), by Rich Lane for Big Switch Networks (2015)
-* [_Building a simple JIT in
- Rust_](https://www.sophiajt.com/building-a-simple-jit-in-rust),
- by Sophia Turner (2015)
-* [bpfjit](https://github.com/polachok/bpfjit) (also [on
- crates.io](https://crates.io/crates/bpfjit)), a Rust crate exporting the cBPF
- JIT compiler from FreeBSD 10 tree to Rust, by Alexander Polakov (2016)
-## Other resources
-* Cilium project documentation about BPF: [_BPF and XDP Reference
- Guide_](http://docs.cilium.io/en/latest/bpf/)
-* [Kernel documentation about BPF](https://docs.kernel.org/bpf/)
-* [_Dive into BPF: a list of reading
- material_](https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf),
- a blog article listing documentation for BPF and related technologies (2016)
-* [The Rust programming language](https://www.rust-lang.org)
@@ -1 +0,0 @@
-doc-valid-idents = ["eBPF", "uBPF"]
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: (Apache-2.0 OR MIT)
-// Copyright 2017 6WIND S.A. <quentin.monnet@6wind.com>
-use rbpf::disassembler;
-// Simply disassemble a program into human-readable instructions.
- 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x79, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2d, 0x23, 0x12, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x69, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x10, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x0e,
- 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf,
- 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
- 0x15, 0x02, 0x08, 0x00, 0x99, 0x99, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0xff,
- 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x21, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x18, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x21, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- disassembler::disassemble(prog);
@@ -1,3 +0,0 @@
- rbpf::helpers::show_helper();
@@ -1,115 +0,0 @@
-// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
-#![allow(clippy::unreadable_literal)]
-// The following example uses an ELF file that has been compiled from the C program available in
-// `load_elf__block_a_port.c` in the same directory.
-//
-// It was compiled with the following command:
-// ```bash
-// clang -O2 -emit-llvm -c load_elf__block_a_port.c -o - | \
-// llc -march=bpf -filetype=obj -o load_elf__block_a_port.o
-// ```
-// Once compiled, this program can be injected into Linux kernel, with tc for instance. Sadly, we
-// need to bring some modifications to the generated bytecode in order to run it: the three
-// instructions with opcode 0x61 load data from a packet area as 4-byte words, where we need to
-// load it as 8-bytes double words (0x79). The kernel does the same kind of translation before
-// running the program, but rbpf does not implement this.
-// In addition, the offset at which the pointer to the packet data is stored must be changed: since
-// we use 8 bytes instead of 4 for the start and end addresses of the data packet, we cannot use
-// the offsets produced by clang (0x4c and 0x50), the addresses would overlap. Instead we can use,
-// for example, 0x40 and 0x50.
-// These change were applied with the following script:
-// xxd load_elf__block_a_port.o | sed '
-// s/6112 5000 0000 0000/7912 5000 0000 0000/ ;
-// s/6111 4c00 0000 0000/7911 4000 0000 0000/ ;
-// s/6111 2200 0000 0000/7911 2200 0000 0000/' | xxd -r > load_elf__block_a_port.tmp
-// mv load_elf__block_a_port.tmp load_elf__block_a_port.o
-// The eBPF program was placed into the `.classifier` ELF section (see C code above), which means
-// that you can retrieve the raw bytecode with `readelf -x .classifier load_elf__block_a_port.o` or
-// with `objdump -s -j .classifier load_elf__block_a_port.o`.
-// Once the bytecode has been edited, we can load the bytecode directly from the ELF object file.
- let file = match elf::File::open_path(path) {
- let packet1 = &mut [
- 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x08,
- 0x00, // ethertype
- 0xa6, 0xab, 0x40, 0x00, 0x40, 0x06, 0x96, 0x0f, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00,
- 0x01,
- // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0.
- 0xd1, 0xe5, 0xc4, 0x9d, 0xd4, 0x30, 0xb5, 0xd2, 0x80, 0x18, 0x01, 0x56, 0xfe, 0x2f, 0x00,
- 0x00, 0x01, 0x01, 0x08, 0x0a, // start data
- 0x00, 0x23, 0x75, 0x89, 0x00, 0x23, 0x63, 0x2d, 0x71, 0x64, 0x66, 0x73, 0x64, 0x66, 0x0au8,
- let packet2 = &mut [
- 0x98, 0x76, 0xc6, 0xcc, // start tcp_hdr
- vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helpers::bpf_trace_printf)
- .unwrap();
- let res = vm.execute_program(packet1).unwrap();
- println!("Packet #1, program returned: {res:?} ({res:#x})");
- assert_eq!(res, 0xffffffff);
- #[cfg(not(windows))]
- {
- let res = unsafe { vm.execute_program_jit(packet2).unwrap() };
- println!("Packet #2, program returned: {res:?} ({res:#x})");
- assert_eq!(res, 0);
- #[cfg(windows)]
- let res = vm.execute_program(packet2).unwrap();
- println!("Packet #2, program returned: {:?} ({:#x})", res, res);
@@ -1,43 +0,0 @@
-// SPDX-License-Identifier: (APACHE-2.0 OR MIT)
-// Block TCP packets on source or destination port 0x9999.
-#include <linux/ip.h>
-#include <linux/in.h>
-#include <linux/tcp.h>
-#include <linux/bpf.h>
-#define ETH_ALEN 6
-#define ETH_P_IP 0x0008 /* htons(0x0800) */
-#define TCP_HDR_LEN 20
-#define BLOCKED_TCP_PORT 0x9999
-struct eth_hdr {
- unsigned char h_dest[ETH_ALEN];
- unsigned char h_source[ETH_ALEN];
- unsigned short h_proto;
-};
-#define SEC(NAME) __attribute__((section(NAME), used))
-SEC(".classifier")
-int handle_ingress(struct __sk_buff *skb)
-{
- void *data = (void *)(long)skb->data;
- void *data_end = (void *)(long)skb->data_end;
- struct eth_hdr *eth = data;
- struct iphdr *iph = data + sizeof(*eth);
- struct tcphdr *tcp = data + sizeof(*eth) + sizeof(*iph);
- /* single length check */
- if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcp) > data_end)
- return 0;
- if (eth->h_proto != ETH_P_IP)
- if (iph->protocol != IPPROTO_TCP)
- if (tcp->source == BLOCKED_TCP_PORT || tcp->dest == BLOCKED_TCP_PORT)
- return -1;
@@ -1,126 +0,0 @@
-// Copyright Microsoft Corporation
-// Path: examples/rbpf_plugin.rs
-use std::io::Read;
-// Helper function used by https://github.com/Alan-Jowett/bpf_conformance/blob/main/tests/call_unwind_fail.data
-fn _unwind(a: u64, _b: u64, _c: u64, _d: u64, _e: u64) -> u64 {
- a
-// This is a plugin for the bpf_conformance test suite (https://github.com/Alan-Jowett/bpf_conformance)
-// It accepts a single argument, the memory contents to pass to the VM.
-// It reads the program from stdin.
- let mut args: Vec<String> = std::env::args().collect();
- #[allow(unused_mut)] // In no_std the jit variable isn't mutated.
- let mut jit: bool = false;
- let mut cranelift: bool = false;
- let mut program_text = String::new();
- let mut memory_text = String::new();
- args.remove(0);
- // Memory is always the first argument.
- if !args.is_empty() {
- memory_text.clone_from(&args[0]);
- // Strip whitespace
- memory_text.retain(|c| !c.is_whitespace());
- // Process the rest of the arguments.
- while !args.is_empty() {
- match args[0].as_str() {
- "--help" => {
- println!("Usage: rbpf_plugin [memory] < program");
- return;
- "--jit" => {
- #[cfg(any(windows, not(feature = "std")))]
- println!("JIT not supported");
- #[cfg(all(not(windows), feature = "std"))]
- jit = true;
- "--cranelift" => {
- cranelift = true;
- #[cfg(not(feature = "cranelift"))]
- let _ = cranelift;
- println!("Cranelift is not enabled");
- "--program" => {
- if args.len() < 2 {
- println!("Missing argument to --program");
- program_text.clone_from(&args[0]);
- _ => panic!("Unknown argument {}", args[0]),
- if program_text.is_empty() {
- // Read program text from stdin
- std::io::stdin().read_to_string(&mut program_text).unwrap();
- program_text.retain(|c| !c.is_whitespace());
- // Convert program from hex to bytecode
- let bytecode = hex::decode(program_text).unwrap();
- // Convert memory from hex to bytes
- let mut memory: Vec<u8> = hex::decode(memory_text).unwrap();
- // Create rbpf vm
- let mut vm = rbpf::EbpfVmRaw::new(Some(&bytecode)).unwrap();
- // Register the helper function used by call_unwind_fail.data test.
- vm.register_helper(5, _unwind).unwrap();
- let result: u64;
- if jit {
- result = vm.execute_program_jit(&mut memory).unwrap();
- } else if cranelift {
- #[cfg(feature = "cranelift")]
- vm.cranelift_compile().unwrap();
- result = vm.execute_program_cranelift(&mut memory).unwrap();
- } else {
- result = vm.execute_program(&mut memory).unwrap();
- println!("{result:x}");
@@ -1,74 +0,0 @@
-#[macro_use]
-extern crate json;
-// Turn a program into a JSON string.
-// Relies on `json` crate.
-// You may copy this function and adapt it according to your needs. For instance, you may want to:
-// * Remove the "desc" (description) attributes from the output.
-// * Print integers as integers, and not as strings containing their hexadecimal representation
-// (just replace the relevant `format!()` calls by the commented values.
-fn to_json(prog: &[u8]) -> String {
- // This call returns a high-level representation of the instructions, with the two parts of
- // `LD_DW_IMM` instructions merged, and name and descriptions of the instructions.
- // If you prefer to use a lower-level representation, use `ebpf::to_insn_vec()` function
- // instead.
- let insns = disassembler::to_insn_vec(prog);
- let mut json_insns = vec![];
- for insn in insns {
- json_insns.push(object!(
- "opc" => format!("{:#x}", insn.opc), // => insn.opc,
- "dst" => format!("{:#x}", insn.dst), // => insn.dst,
- "src" => format!("{:#x}", insn.src), // => insn.src,
- "off" => format!("{:#x}", insn.off), // => insn.off,
- // Warning: for imm we use a i64 instead of a i32 (to have correct values for
- // `lddw` operation. If we print a number in the JSON this is not a problem, the
- // internal i64 has the same value with extended sign on 32 most significant bytes.
- // If we print the hexadecimal value as a string however, we want to cast as a i32
- // to prevent all other instructions to print spurious `ffffffff` prefix if the
- // number is negative. When values takes more than 32 bits with `lddw`, the cast
- // has no effect and the complete value is printed anyway.
- "imm" => format!("{:#x}", insn.imm as i32), // => insn.imm,
- "desc" => insn.desc
- ));
- json::stringify_pretty(
- object!(
- "size" => json_insns.len(),
- "insns" => json_insns
- ),
- 4,
- )
-// Load a program from an object file, and prints it to standard output as a JSON string.
- // Let's reuse this file from `load_elf/example`.
- println!("{}", to_json(prog));
-// The main objectives of this example is to show:
-// * the use of EbpfVmNoData function,
-// * and the use of a helper.
-// The two eBPF programs are independent and are not related to one another.
- let prog1 = &[
- 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit and return r0
- // We use helper `bpf_time_getns()`, which is similar to helper `bpf_ktime_getns()` from Linux
- // kernel. Hence rbpf::helpers module provides the index of this in-kernel helper as a
- // constant, so that we can remain compatible with programs for the kernel. Here we also cast
- // it to a u8 so as to use it directly in program instructions.
- let hkey = helpers::BPF_KTIME_GETNS_IDX as u8;
- let prog2 = &[
- 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0
- 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0
- 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0
- 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0
- 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0
- 0x85, 0x00, 0x00, 0x00, hkey, 0x00, 0x00, 0x00, // call helper <hkey>
- // Create a VM: this one takes no data. Load prog1 in it.
- let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap();
- // Execute prog1.
- // As struct EbpfVmNoData does not takes any memory area, its return value is mostly
- // deterministic. So we know prog1 will always return 3. There is an exception: when it uses
- // helpers, the latter may have non-deterministic values, and all calls may not return the same
- // value.
- //
- // In the following example we use a helper to get the elapsed time since boot time: we
- // reimplement uptime in eBPF, in Rust. Because why not.
- vm.set_program(prog2).unwrap();
- vm.register_helper(helpers::BPF_KTIME_GETNS_IDX, helpers::bpf_time_getns)
- let time;
- time = unsafe { vm.execute_program_jit().unwrap() };
- time = vm.execute_program().unwrap();
- let days = time / 10u64.pow(9) / 60 / 60 / 24;
- let hours = (time / 10u64.pow(9) / 60 / 60) % 24;
- let minutes = (time / 10u64.pow(9) / 60) % 60;
- let seconds = (time / 10u64.pow(9)) % 60;
- let nanosec = time % 10u64.pow(9);
- println!(
- "Uptime: {:#x} ns == {} days {:02}:{:02}:{:02}, {} ns",
- time, days, hours, minutes, seconds, nanosec
- );
@@ -1,72 +0,0 @@
-echo on
-SetLocal EnableDelayedExpansion
-REM This is the recommended way to choose the toolchain version, according to
-REM Appveyor's documentation.
-SET PATH=C:\Program Files (x86)\MSBuild\%TOOLCHAIN_VERSION%\Bin;%PATH%
-set VCVARSALL="C:\Program Files (x86)\Microsoft Visual Studio %TOOLCHAIN_VERSION%\VC\vcvarsall.bat"
-if [%Platform%] NEQ [x64] goto win32
-set TARGET_ARCH=x86_64
-set TARGET_PROGRAM_FILES=%ProgramFiles%
-call %VCVARSALL% amd64
-if %ERRORLEVEL% NEQ 0 exit 1
-goto download
-:win32
-if [%Platform%] NEQ [Win32] exit 1
-set TARGET_ARCH=i686
-set TARGET_PROGRAM_FILES=%ProgramFiles(x86)%
-call %VCVARSALL% amd64_x86
-:download
-REM vcvarsall turns echo off
-mkdir windows_build_tools
-mkdir windows_build_tools\
-echo Downloading Yasm...
-powershell -Command "(New-Object Net.WebClient).DownloadFile('http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe', 'windows_build_tools\yasm.exe')"
-if %ERRORLEVEL% NEQ 0 (
- echo ...downloading Yasm failed.
- exit 1
-)
-set RUST_URL=https://static.rust-lang.org/dist/rust-%RUST%-%TARGET_ARCH%-pc-windows-msvc.msi
-echo Downloading %RUST_URL%...
-mkdir build
-powershell -Command "(New-Object Net.WebClient).DownloadFile('%RUST_URL%', 'build\rust-%RUST%-%TARGET_ARCH%-pc-windows-msvc.msi')"
- echo ...downloading Rust failed.
-start /wait msiexec /i build\rust-%RUST%-%TARGET_ARCH%-pc-windows-msvc.msi INSTALLDIR="%TARGET_PROGRAM_FILES%\Rust %RUST%" /quiet /qn /norestart
-set PATH="%TARGET_PROGRAM_FILES%\Rust %RUST%\bin";%cd%\windows_build_tools;%PATH%
-if [%Configuration%] == [Release] set CARGO_MODE=--release
-set
-link /?
-cl /?
-rustc --version
-cargo --version
-cargo test --all-features -vv %CARGO_MODE%
-REM Verify that `cargo build`, independent from `cargo test`, works; i.e.
-REM verify that non-test builds aren't trying to use test-only features.
-cargo build -vv %CARGO_MODE%
-REM Verify that we can build with all features
-cargo build --all-features -vv %CARGO_MODE%
-group_imports="StdExternalCrate"
-reorder_imports=true
-imports_granularity="Crate"
@@ -1,642 +0,0 @@
-// Copyright 2017 Rich Lane <lanerl@gmail.com>
-// Rust-doc comments were left in the module, but it is no longer publicly exposed from the root
-// file of the crate. Do not expect to find those comments in the documentation of the crate.
-//! This module parses eBPF assembly language source code.
-use alloc::{
- string::{String, ToString},
- vec::Vec,
-#[cfg(feature = "std")]
-use combine::EasyParser;
-use combine::{
- attempt, between, eof, many, many1, one_of, optional,
- parser::char::{alpha_num, char, digit, hex_digit, spaces, string},
- sep_by,
- stream::position::{self},
- ParseError, Parser, Stream,
-/// Operand of an instruction.
-#[derive(Clone, Copy, Debug, PartialEq, Eq)]
-pub enum Operand {
- /// Register number.
- Register(i64),
- /// Jump offset or immediate.
- Integer(i64),
- /// Register number and offset.
- Memory(i64, i64),
- /// Used for pattern matching.
- Nil,
-/// Parsed instruction.
-#[derive(Debug, PartialEq, Eq)]
-pub struct Instruction {
- /// Instruction name.
- pub name: String,
- /// Operands.
- pub operands: Vec<Operand>,
-fn ident<I>() -> impl Parser<I, Output = String>
-where
- I: Stream<Token = char>,
- I::Error: ParseError<I::Token, I::Range, I::Position>,
- many1(alpha_num())
-fn integer<I>() -> impl Parser<I, Output = i64>
- let sign = optional(one_of("-+".chars())).map(|x| match x {
- Some('-') => -1,
- _ => 1,
- });
- let hex = string("0x")
- .with(many1(hex_digit()))
- .map(|x: String| u64::from_str_radix(&x, 16).unwrap() as i64);
- let dec = many1(digit()).map(|x: String| x.parse::<i64>().unwrap());
- (sign, attempt(hex).or(dec)).map(|(s, x)| s * x)
-fn register<I>() -> impl Parser<I, Output = i64>
- char('r')
- .with(many1(digit()))
- .map(|x: String| x.parse::<i64>().unwrap())
-fn operand<I>() -> impl Parser<I, Output = Operand>
- let register_operand = register().map(Operand::Register);
- let immediate = integer().map(Operand::Integer);
- let memory = between(char('['), char(']'), (register(), optional(integer())))
- .map(|t| Operand::Memory(t.0, t.1.unwrap_or(0)));
- register_operand.or(immediate).or(memory)
-fn instruction<I>() -> impl Parser<I, Output = Instruction>
- let operands = sep_by(operand(), char(',').skip(spaces()));
- (ident().skip(spaces()), operands, spaces()).map(|t| Instruction {
- name: t.0,
- operands: t.1,
- })
-/// Parse a string into a list of instructions.
-///
-/// The instructions are not validated and may have invalid names and operand types.
-pub fn parse(input: &str) -> Result<Vec<Instruction>, String> {
- let mut with = spaces().with(many(instruction()).skip(eof()));
- #[cfg(feature = "std")]
- match with.easy_parse(position::Stream::new(input)) {
- Ok((insts, _)) => Ok(insts),
- Err(err) => Err(err.to_string()),
- #[cfg(not(feature = "std"))]
- match with.parse(position::Stream::new(input)) {
-#[cfg(test)]
-mod tests {
- use alloc::{string::ToString, vec};
- use combine::Parser;
- use super::{ident, instruction, integer, operand, parse, register, Instruction, Operand};
- // Unit tests for the different kinds of parsers.
- #[test]
- fn test_ident() {
- assert_eq!(ident().parse("nop"), Ok(("nop".to_string(), "")));
- assert_eq!(ident().parse("add32"), Ok(("add32".to_string(), "")));
- assert_eq!(ident().parse("add32*"), Ok(("add32".to_string(), "*")));
- fn test_integer() {
- assert_eq!(integer().parse("0"), Ok((0, "")));
- assert_eq!(integer().parse("42"), Ok((42, "")));
- assert_eq!(integer().parse("+42"), Ok((42, "")));
- assert_eq!(integer().parse("-42"), Ok((-42, "")));
- assert_eq!(integer().parse("0x0"), Ok((0, "")));
- assert_eq!(
- integer().parse("0x123456789abcdef0"),
- Ok((0x123456789abcdef0, ""))
- assert_eq!(integer().parse("-0x1f"), Ok((-31, "")));
- fn test_register() {
- assert_eq!(register().parse("r0"), Ok((0, "")));
- assert_eq!(register().parse("r15"), Ok((15, "")));
- fn test_operand() {
- assert_eq!(operand().parse("r0"), Ok((Operand::Register(0), "")));
- assert_eq!(operand().parse("r15"), Ok((Operand::Register(15), "")));
- assert_eq!(operand().parse("0"), Ok((Operand::Integer(0), "")));
- assert_eq!(operand().parse("42"), Ok((Operand::Integer(42), "")));
- assert_eq!(operand().parse("[r1]"), Ok((Operand::Memory(1, 0), "")));
- assert_eq!(operand().parse("[r3+5]"), Ok((Operand::Memory(3, 5), "")));
- operand().parse("[r3+0x1f]"),
- Ok((Operand::Memory(3, 31), ""))
- operand().parse("[r3-0x1f]"),
- Ok((Operand::Memory(3, -31), ""))
- fn test_instruction() {
- instruction().parse("exit"),
- Ok((
- Instruction {
- name: "exit".to_string(),
- operands: vec![],
- },
- ""
- ))
- instruction().parse("call 2"),
- name: "call".to_string(),
- operands: vec![Operand::Integer(2)],
- instruction().parse("addi r1, 2"),
- name: "addi".to_string(),
- operands: vec![Operand::Register(1), Operand::Integer(2)],
- instruction().parse("ldxb r2, [r1+12]"),
- name: "ldxb".to_string(),
- operands: vec![Operand::Register(2), Operand::Memory(1, 12)],
- instruction().parse("lsh r3, 0x8"),
- name: "lsh".to_string(),
- operands: vec![Operand::Register(3), Operand::Integer(8)],
- instruction().parse("jne r3, 0x8, +37"),
- name: "jne".to_string(),
- operands: vec![
- Operand::Register(3),
- Operand::Integer(8),
- Operand::Integer(37)
- ],
- // Whitespace between operands is optional.
- instruction().parse("jne r3,0x8,+37"),
- // Other unit tests: try to parse various set of instructions.
- fn test_empty() {
- assert_eq!(parse(""), Ok(vec![]));
- fn test_exit() {
- // No operands.
- parse("exit"),
- Ok(vec![Instruction {
- }])
- fn test_lsh() {
- // Register and immediate operands.
- parse("lsh r3, 0x20"),
- operands: vec![Operand::Register(3), Operand::Integer(0x20)],
- fn test_ja() {
- // Jump offset operand.
- parse("ja +1"),
- name: "ja".to_string(),
- operands: vec![Operand::Integer(1)],
- fn test_ldxh() {
- // Register and memory operands.
- parse("ldxh r4, [r1+12]"),
- name: "ldxh".to_string(),
- operands: vec![Operand::Register(4), Operand::Memory(1, 12)],
- fn test_tcp_sack() {
- // Sample program from ubpf.
- // We could technically indent the instructions since the parser support white spaces at
- // the beginning, but there is another test for that.
- let src = "\
-ldxb r2, [r1+12]
-ldxb r3, [r1+13]
-lsh r3, 0x8
-or r3, r2
-mov r0, 0x0
-jne r3, 0x8, +37
-ldxb r2, [r1+23]
-jne r2, 0x6, +35
-ldxb r2, [r1+14]
-add r1, 0xe
-and r2, 0xf
-lsh r2, 0x2
-add r1, r2
-ldxh r4, [r1+12]
-add r1, 0x14
-rsh r4, 0x2
-and r4, 0x3c
-mov r2, r4
-add r2, 0xffffffec
-mov r5, 0x15
-mov r3, 0x0
-jgt r5, r4, +20
-mov r5, r3
-lsh r5, 0x20
-arsh r5, 0x20
-mov r4, r1
-add r4, r5
-ldxb r5, [r4]
-jeq r5, 0x1, +4
-jeq r5, 0x0, +12
-mov r6, r3
-jeq r5, 0x5, +9
-ja +2
-add r3, 0x1
-ldxb r3, [r4+1]
-add r3, r6
-lsh r3, 0x20
-arsh r3, 0x20
-jsgt r2, r3, -18
-ja +1
-mov r0, 0x1
-";
- parse(src),
- Ok(vec![
- operands: vec![Operand::Register(3), Operand::Memory(1, 13)],
- name: "or".to_string(),
- operands: vec![Operand::Register(3), Operand::Register(2)],
- name: "mov".to_string(),
- operands: vec![Operand::Register(0), Operand::Integer(0)],
- operands: vec![Operand::Register(2), Operand::Memory(1, 23)],
- Operand::Register(2),
- Operand::Integer(6),
- Operand::Integer(35)
- operands: vec![Operand::Register(2), Operand::Memory(1, 14)],
- name: "add".to_string(),
- operands: vec![Operand::Register(1), Operand::Integer(14)],
- name: "and".to_string(),
- operands: vec![Operand::Register(2), Operand::Integer(15)],
- operands: vec![Operand::Register(2), Operand::Integer(2)],
- operands: vec![Operand::Register(1), Operand::Register(2)],
- operands: vec![Operand::Register(1), Operand::Integer(20)],
- name: "rsh".to_string(),
- operands: vec![Operand::Register(4), Operand::Integer(2)],
- operands: vec![Operand::Register(4), Operand::Integer(60)],
- operands: vec![Operand::Register(2), Operand::Register(4)],
- operands: vec![Operand::Register(2), Operand::Integer(4294967276)],
- operands: vec![Operand::Register(5), Operand::Integer(21)],
- operands: vec![Operand::Register(3), Operand::Integer(0)],
- name: "jgt".to_string(),
- Operand::Register(5),
- Operand::Register(4),
- Operand::Integer(20)
- operands: vec![Operand::Register(5), Operand::Register(3)],
- operands: vec![Operand::Register(5), Operand::Integer(32)],
- name: "arsh".to_string(),
- operands: vec![Operand::Register(4), Operand::Register(1)],
- operands: vec![Operand::Register(4), Operand::Register(5)],
- operands: vec![Operand::Register(5), Operand::Memory(4, 0)],
- name: "jeq".to_string(),
- Operand::Integer(1),
- Operand::Integer(4)
- Operand::Integer(0),
- Operand::Integer(12)
- operands: vec![Operand::Register(6), Operand::Register(3)],
- Operand::Integer(5),
- Operand::Integer(9)
- operands: vec![Operand::Register(3), Operand::Integer(1)],
- operands: vec![Operand::Register(3), Operand::Memory(4, 1)],
- operands: vec![Operand::Register(3), Operand::Register(6)],
- operands: vec![Operand::Register(3), Operand::Integer(32)],
- name: "jsgt".to_string(),
- Operand::Integer(-18)
- operands: vec![Operand::Register(0), Operand::Integer(1)],
- ])
- /// When running without `std` the `EasyParser` provided by `combine`
- /// cannot be used. Because of this we need to use the `Parser` and the
- /// error messages are different.
- fn test_error_eof() {
- let expected_error;
- expected_error = Err(
- "Parse error at line: 1, column: 6\nUnexpected end of input\nExpected digit\n"
- .to_string(),
- expected_error = Err("unexpected parse".to_string());
- // Unexpected end of input in a register name.
- assert_eq!(parse("lsh r"), expected_error);
- fn test_error_unexpected_character() {
- "Parse error at line: 2, column: 1\nUnexpected `^`\nExpected letter or digit, whitespaces, `r`, `-`, `+`, `[` or end of input\n".to_string()
- // Unexpected character at end of input.
- assert_eq!(parse("exit\n^"), expected_error);
- fn test_initial_whitespace() {
- parse(
- "
- exit"
@@ -1,277 +0,0 @@
-//! This module translates eBPF assembly language to binary.
- collections::BTreeMap,
- format,
- vec,
-use self::InstructionType::{
- AluBinary, AluUnary, Call, Endian, JumpConditional, JumpUnconditional, LoadAbs, LoadImm,
- LoadInd, LoadReg, NoOperand, StoreImm, StoreReg,
-use crate::{
- asm_parser::{
- parse, Instruction, Operand,
- Operand::{Integer, Memory, Nil, Register},
- ebpf::{self, Insn},
-#[derive(Clone, Copy, Debug, PartialEq)]
-enum InstructionType {
- AluBinary,
- AluUnary,
- LoadImm,
- LoadAbs,
- LoadInd,
- LoadReg,
- StoreImm,
- StoreReg,
- JumpUnconditional,
- JumpConditional,
- Call,
- Endian(i64),
- NoOperand,
-fn make_instruction_map() -> BTreeMap<String, (InstructionType, u8)> {
- let mut result = BTreeMap::new();
- let alu_binary_ops = [
- ("add", ebpf::BPF_ADD),
- ("sub", ebpf::BPF_SUB),
- ("mul", ebpf::BPF_MUL),
- ("div", ebpf::BPF_DIV),
- ("or", ebpf::BPF_OR),
- ("and", ebpf::BPF_AND),
- ("lsh", ebpf::BPF_LSH),
- ("rsh", ebpf::BPF_RSH),
- ("mod", ebpf::BPF_MOD),
- ("xor", ebpf::BPF_XOR),
- ("mov", ebpf::BPF_MOV),
- ("arsh", ebpf::BPF_ARSH),
- let mem_sizes = [
- ("w", ebpf::BPF_W),
- ("h", ebpf::BPF_H),
- ("b", ebpf::BPF_B),
- ("dw", ebpf::BPF_DW),
- let jump_conditions = [
- ("jeq", ebpf::BPF_JEQ),
- ("jgt", ebpf::BPF_JGT),
- ("jge", ebpf::BPF_JGE),
- ("jlt", ebpf::BPF_JLT),
- ("jle", ebpf::BPF_JLE),
- ("jset", ebpf::BPF_JSET),
- ("jne", ebpf::BPF_JNE),
- ("jsgt", ebpf::BPF_JSGT),
- ("jsge", ebpf::BPF_JSGE),
- ("jslt", ebpf::BPF_JSLT),
- ("jsle", ebpf::BPF_JSLE),
- let mut entry = |name: &str, inst_type: InstructionType, opc: u8| {
- result.insert(name.to_string(), (inst_type, opc))
- // Miscellaneous.
- entry("exit", NoOperand, ebpf::EXIT);
- entry("ja", JumpUnconditional, ebpf::JA);
- entry("call", Call, ebpf::CALL);
- entry("lddw", LoadImm, ebpf::LD_DW_IMM);
- // AluUnary.
- entry("neg", AluUnary, ebpf::NEG64);
- entry("neg32", AluUnary, ebpf::NEG32);
- entry("neg64", AluUnary, ebpf::NEG64);
- // AluBinary.
- for &(name, opc) in &alu_binary_ops {
- entry(name, AluBinary, ebpf::BPF_ALU64 | opc);
- entry(&format!("{name}32"), AluBinary, ebpf::BPF_ALU | opc);
- entry(&format!("{name}64"), AluBinary, ebpf::BPF_ALU64 | opc);
- // LoadAbs, LoadInd, LoadReg, StoreImm, and StoreReg.
- for &(suffix, size) in &mem_sizes {
- entry(
- &format!("ldabs{suffix}"),
- ebpf::BPF_ABS | ebpf::BPF_LD | size,
- &format!("ldind{suffix}"),
- ebpf::BPF_IND | ebpf::BPF_LD | size,
- &format!("ldx{suffix}"),
- ebpf::BPF_MEM | ebpf::BPF_LDX | size,
- &format!("st{suffix}"),
- ebpf::BPF_MEM | ebpf::BPF_ST | size,
- &format!("stx{suffix}"),
- ebpf::BPF_MEM | ebpf::BPF_STX | size,
- // JumpConditional.
- for &(name, condition) in &jump_conditions {
- entry(name, JumpConditional, ebpf::BPF_JMP | condition);
- &format!("{name}32"),
- ebpf::BPF_JMP32 | condition,
- // Endian.
- for &size in &[16, 32, 64] {
- entry(&format!("be{size}"), Endian(size), ebpf::BE);
- entry(&format!("le{size}"), Endian(size), ebpf::LE);
- result
-fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result<Insn, String> {
- if !(0..16).contains(&dst) {
- return Err(format!("Invalid destination register {dst}"));
- if dst < 0 || src >= 16 {
- return Err(format!("Invalid source register {src}"));
- if !(-32768..32768).contains(&off) {
- return Err(format!("Invalid offset {off}"));
- if !(-2147483648..2147483648).contains(&imm) {
- return Err(format!("Invalid immediate {imm}"));
- Ok(Insn {
- opc,
- dst: dst as u8,
- src: src as u8,
- off: off as i16,
- imm: imm as i32,
-// TODO Use slice patterns when available and remove this function.
-fn operands_tuple(operands: &[Operand]) -> Result<(Operand, Operand, Operand), String> {
- match operands.len() {
- 0 => Ok((Nil, Nil, Nil)),
- 1 => Ok((operands[0], Nil, Nil)),
- 2 => Ok((operands[0], operands[1], Nil)),
- 3 => Ok((operands[0], operands[1], operands[2])),
- _ => Err("Too many operands".to_string()),
-fn encode(inst_type: InstructionType, opc: u8, operands: &[Operand]) -> Result<Insn, String> {
- let (a, b, c) = (operands_tuple(operands))?;
- match (inst_type, a, b, c) {
- (AluBinary, Register(dst), Register(src), Nil) => insn(opc | ebpf::BPF_X, dst, src, 0, 0),
- (AluBinary, Register(dst), Integer(imm), Nil) => insn(opc | ebpf::BPF_K, dst, 0, 0, imm),
- (AluUnary, Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, 0),
- (LoadAbs, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm),
- (LoadInd, Register(src), Integer(imm), Nil) => insn(opc, 0, src, 0, imm),
- (LoadReg, Register(dst), Memory(src, off), Nil)
- | (StoreReg, Memory(dst, off), Register(src), Nil) => insn(opc, dst, src, off, 0),
- (StoreImm, Memory(dst, off), Integer(imm), Nil) => insn(opc, dst, 0, off, imm),
- (NoOperand, Nil, Nil, Nil) => insn(opc, 0, 0, 0, 0),
- (JumpUnconditional, Integer(off), Nil, Nil) => insn(opc, 0, 0, off, 0),
- (JumpConditional, Register(dst), Register(src), Integer(off)) => {
- insn(opc | ebpf::BPF_X, dst, src, off, 0)
- (JumpConditional, Register(dst), Integer(imm), Integer(off)) => {
- insn(opc | ebpf::BPF_K, dst, 0, off, imm)
- (Call, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm),
- (Endian(size), Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, size),
- (LoadImm, Register(dst), Integer(imm), Nil) => insn(opc, dst, 0, 0, (imm << 32) >> 32),
- _ => Err(format!("Unexpected operands: {operands:?}")),
-fn assemble_internal(parsed: &[Instruction]) -> Result<Vec<Insn>, String> {
- let instruction_map = make_instruction_map();
- let mut result: Vec<Insn> = vec![];
- for instruction in parsed {
- let name = instruction.name.as_str();
- match instruction_map.get(name) {
- Some(&(inst_type, opc)) => {
- match encode(inst_type, opc, &instruction.operands) {
- Ok(insn) => result.push(insn),
- Err(msg) => return Err(format!("Failed to encode {name}: {msg}")),
- // Special case for lddw.
- if let LoadImm = inst_type {
- if let Integer(imm) = instruction.operands[1] {
- result.push(insn(0, 0, 0, 0, imm >> 32).unwrap());
- None => return Err(format!("Invalid instruction {name:?}")),
- Ok(result)
-/// Parse assembly source and translate to binary.
-/// # Examples
-/// ```
-/// use rbpf::assembler::assemble;
-/// let prog = assemble("add64 r1, 0x605
-/// mov64 r2, 0x32
-/// mov64 r1, r0
-/// be16 r0
-/// neg64 r2
-/// exit");
-/// println!("{:?}", prog);
-/// # assert_eq!(prog,
-/// # Ok(vec![0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
-/// # 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
-/// # 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/// # 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
-/// # 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/// # 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
-/// This will produce the following output:
-/// ```test
-/// Ok([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
-/// 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
-/// 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
-/// 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
-pub fn assemble(src: &str) -> Result<Vec<u8>, String> {
- let parsed = (parse(src))?;
- let insns = (assemble_internal(&parsed))?;
- let mut result: Vec<u8> = vec![];
- result.extend_from_slice(&insn.to_array());
@@ -1,1230 +0,0 @@
-use alloc::{collections::BTreeMap, format, vec, vec::Vec};
-use core::{mem, mem::ManuallyDrop};
-use std::io::ErrorKind;
-use cranelift_codegen::{
- entity::EntityRef,
- ir::{
- condcodes::IntCC,
- types::{I16, I32, I64, I8},
- AbiParam, Block, Endianness, FuncRef, Function, InstBuilder, MemFlags, Signature,
- SourceLoc, StackSlotData, StackSlotKind, TrapCode, Type, UserFuncName, Value,
- isa::OwnedTargetIsa,
- settings::{self, Configurable},
-use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
-use cranelift_jit::{JITBuilder, JITModule};
-use cranelift_module::{FuncId, Linkage, Module};
-use super::{Error, HashMap, HashSet};
-use crate::ebpf::{
- self, Insn, BPF_ALU_OP_MASK, BPF_IND, BPF_JEQ, BPF_JGE, BPF_JGT, BPF_JLE, BPF_JLT, BPF_JMP32,
- BPF_JNE, BPF_JSET, BPF_JSGE, BPF_JSGT, BPF_JSLE, BPF_JSLT, BPF_X, STACK_SIZE,
-pub type JittedFunction = extern "C" fn(
- *mut u8, // mem_ptr
- usize, // mem_len
- *mut u8, // mbuff_ptr
- usize, // mbuff_len
-) -> u64;
-pub(crate) struct CraneliftCompiler {
- isa: OwnedTargetIsa,
- module: JITModule,
- helpers: HashMap<u32, ebpf::Helper>,
- helper_func_refs: HashMap<u32, FuncRef>,
- /// List of blocks corresponding to each instruction.
- /// We only store the first instruction that observes a new block
- insn_blocks: BTreeMap<u32, Block>,
- /// Map of block targets for each jump/branching instruction.
- insn_targets: BTreeMap<u32, (Block, Block)>,
- filled_blocks: HashSet<Block>,
- /// Map of register numbers to Cranelift variables.
- registers: [Variable; 11],
- /// Other usefull variables used throughout the program.
- mem_start: Variable,
- mem_end: Variable,
- mbuf_start: Variable,
- mbuf_end: Variable,
- stack_start: Variable,
- stack_end: Variable,
-impl CraneliftCompiler {
- pub(crate) fn new(helpers: HashMap<u32, ebpf::Helper>) -> Self {
- let mut flag_builder = settings::builder();
- flag_builder.set("opt_level", "speed").unwrap();
- // Enable stack probes
- flag_builder.enable("enable_probestack").unwrap();
- flag_builder.set("probestack_strategy", "inline").unwrap();
- let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
- panic!("host machine is not supported: {}", msg);
- let isa = isa_builder
- .finish(settings::Flags::new(flag_builder))
- let mut jit_builder =
- JITBuilder::with_isa(isa.clone(), cranelift_module::default_libcall_names());
- // Register all the helpers
- for (k, v) in helpers.iter() {
- let name = format!("helper_{}", k);
- jit_builder.symbol(name, (*v) as usize as *const u8);
- let mut module = JITModule::new(jit_builder);
- let registers = (0..11)
- .map(|i| Variable::new(i))
- .collect::<Vec<_>>()
- .try_into()
- Self {
- isa,
- module,
- helpers,
- helper_func_refs: HashMap::new(),
- insn_blocks: BTreeMap::new(),
- insn_targets: BTreeMap::new(),
- filled_blocks: HashSet::new(),
- registers,
- mem_start: Variable::new(11),
- mem_end: Variable::new(12),
- mbuf_start: Variable::new(13),
- mbuf_end: Variable::new(14),
- stack_start: Variable::new(15),
- stack_end: Variable::new(16),
- pub(crate) fn compile_function(mut self, prog: &[u8]) -> Result<CraneliftProgram, Error> {
- let name = "main";
- // This is not a standard eBPF function! We use an informal ABI with just 4 parameters.
- // See [JittedFunction] which is the signature of this function.
- // Since this function only serves as the entrypoint for the JITed program, it doesen't
- // really matter.
- let sig = Signature {
- params: vec![
- AbiParam::new(I64),
- returns: vec![AbiParam::new(I64)],
- call_conv: self.isa.default_call_conv(),
- let func_id = self
- .module
- .declare_function(name, Linkage::Local, &sig)
- let mut ctx = self.module.make_context();
- ctx.func = Function::with_name_signature(UserFuncName::testcase(name.as_bytes()), sig);
- let mut func_ctx = FunctionBuilderContext::new();
- let mut builder: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
- let entry = builder.create_block();
- builder.append_block_params_for_function_params(entry);
- builder.switch_to_block(entry);
- self.build_cfg(&mut builder, prog)?;
- self.build_function_prelude(&mut builder, entry)?;
- self.translate_program(&mut builder, prog)?;
- builder.seal_all_blocks();
- builder.finalize();
- self.module.define_function(func_id, &mut ctx).unwrap();
- self.module.finalize_definitions().unwrap();
- self.module.clear_context(&mut ctx);
- Ok(CraneliftProgram::new(self.module, func_id))
- fn build_function_prelude(
- &mut self,
- bcx: &mut FunctionBuilder,
- entry: Block,
- ) -> Result<(), Error> {
- // Register the VM registers as variables
- for var in self.registers.iter() {
- bcx.declare_var(*var, I64);
- // Register the bounds check variables
- bcx.declare_var(self.mem_start, I64);
- bcx.declare_var(self.mem_end, I64);
- bcx.declare_var(self.mbuf_start, I64);
- bcx.declare_var(self.mbuf_end, I64);
- bcx.declare_var(self.stack_start, I64);
- bcx.declare_var(self.stack_end, I64);
- // Register the helpers
- for (k, _) in self.helpers.iter() {
- .declare_function(&name, Linkage::Import, &sig)
- let func_ref = self.module.declare_func_in_func(func_id, bcx.func);
- self.helper_func_refs.insert(*k, func_ref);
- // Register the stack
- let ss = bcx.create_sized_stack_slot(StackSlotData {
- kind: StackSlotKind::ExplicitSlot,
- size: STACK_SIZE as u32,
- let addr_ty = self.isa.pointer_type();
- let stack_addr = bcx.ins().stack_addr(addr_ty, ss, STACK_SIZE as i32);
- bcx.def_var(self.registers[10], stack_addr);
- // Initialize the bounds check variables
- let stack_start = bcx.ins().stack_addr(addr_ty, ss, 0);
- bcx.def_var(self.stack_start, stack_start);
- let stack_end = bcx.ins().stack_addr(addr_ty, ss, STACK_SIZE as i32);
- bcx.def_var(self.stack_end, stack_end);
- // This is our internal ABI where the first 2 params are the memory
- let mem_start = bcx.block_params(entry)[0];
- let mem_len = bcx.block_params(entry)[1];
- let mem_end = bcx.ins().iadd(mem_start, mem_len);
- bcx.def_var(self.mem_start, mem_start);
- bcx.def_var(self.mem_end, mem_end);
- // And the next 2 are the mbuf
- let mbuf_start = bcx.block_params(entry)[2];
- let mbuf_len = bcx.block_params(entry)[3];
- let mbuf_end = bcx.ins().iadd(mbuf_start, mbuf_len);
- bcx.def_var(self.mbuf_start, mbuf_start);
- bcx.def_var(self.mbuf_end, mbuf_end);
- // The ABI for eBPF specifies that R1 must contain either the memory, or mbuff pointer
- // If the mbuf length is non-zero, then we use that, otherwise we use the memory pointer
- let mbuf_exists = bcx.ins().icmp_imm(IntCC::NotEqual, mbuf_len, 0);
- let mem_or_mbuf = bcx.ins().select(mbuf_exists, mbuf_start, mem_start);
- bcx.def_var(self.registers[1], mem_or_mbuf);
- // R2 should contain the length of the memory or mbuf
- // At least ebpf-conformance tests expect this
- let mem_or_mbuf_len = bcx.ins().select(mbuf_exists, mbuf_len, mem_len);
- bcx.def_var(self.registers[2], mem_or_mbuf_len);
- // Insert the *actual* initial block
- let program_entry = bcx.create_block();
- bcx.ins().jump(program_entry, &[]);
- self.filled_blocks.insert(bcx.current_block().unwrap());
- self.insn_blocks.insert(0, program_entry);
- Ok(())
- fn translate_program(&mut self, bcx: &mut FunctionBuilder, prog: &[u8]) -> Result<(), Error> {
- let mut insn_ptr: usize = 0;
- while insn_ptr * ebpf::INSN_SIZE < prog.len() {
- let insn = ebpf::get_insn(prog, insn_ptr);
- // If this instruction is on a new block switch to it.
- if let Some(block) = self.insn_blocks.get(&(insn_ptr as u32)) {
- // Blocks must have a terminator instruction at the end before we switch away from them
- let current_block = bcx.current_block().unwrap();
- if !self.filled_blocks.contains(¤t_block) {
- bcx.ins().jump(*block, &[]);
- bcx.switch_to_block(*block);
- // Set the source location for the instruction
- bcx.set_srcloc(SourceLoc::new(insn_ptr as u32));
- match insn.opc {
- // BPF_LD class
- // LD_ABS_* and LD_IND_* are supposed to load pointer to data from metadata buffer.
- // Since this pointer is constant, and since we already know it (mem), do not
- // bother re-fetching it, just use mem already.
- ebpf::LD_ABS_B
- | ebpf::LD_ABS_H
- | ebpf::LD_ABS_W
- | ebpf::LD_ABS_DW
- | ebpf::LD_IND_B
- | ebpf::LD_IND_H
- | ebpf::LD_IND_W
- | ebpf::LD_IND_DW => {
- let ty = match insn.opc {
- ebpf::LD_ABS_B | ebpf::LD_IND_B => I8,
- ebpf::LD_ABS_H | ebpf::LD_IND_H => I16,
- ebpf::LD_ABS_W | ebpf::LD_IND_W => I32,
- ebpf::LD_ABS_DW | ebpf::LD_IND_DW => I64,
- _ => unreachable!(),
- // Both instructions add the imm part of the instruction to the pointer
- let ptr = bcx.use_var(self.mem_start);
- let offset = bcx
- .ins()
- .iconst(self.isa.pointer_type(), insn.imm as u32 as i64);
- let addr = bcx.ins().iadd(ptr, offset);
- // IND instructions additionally add the value of the source register
- let is_ind = (insn.opc & BPF_IND) != 0;
- let addr = if is_ind {
- let src_reg = self.insn_src(bcx, &insn);
- bcx.ins().iadd(addr, src_reg)
- addr
- // The offset here has already been added to the pointer, so we pass 0
- let loaded = self.reg_load(bcx, ty, addr, 0);
- let ext = if ty != I64 {
- bcx.ins().uextend(I64, loaded)
- loaded
- self.set_dst(bcx, &insn, ext);
- ebpf::LD_DW_IMM => {
- insn_ptr += 1;
- let next_insn = ebpf::get_insn(prog, insn_ptr);
- let imm = (((insn.imm as u32) as u64) + ((next_insn.imm as u64) << 32)) as i64;
- let iconst = bcx.ins().iconst(I64, imm);
- self.set_dst(bcx, &insn, iconst);
- // BPF_LDX class
- ebpf::LD_B_REG | ebpf::LD_H_REG | ebpf::LD_W_REG | ebpf::LD_DW_REG => {
- ebpf::LD_B_REG => I8,
- ebpf::LD_H_REG => I16,
- ebpf::LD_W_REG => I32,
- ebpf::LD_DW_REG => I64,
- let base = self.insn_src(bcx, &insn);
- let loaded = self.reg_load(bcx, ty, base, insn.off);
- // BPF_ST and BPF_STX class
- ebpf::ST_B_IMM
- | ebpf::ST_H_IMM
- | ebpf::ST_W_IMM
- | ebpf::ST_DW_IMM
- | ebpf::ST_B_REG
- | ebpf::ST_H_REG
- | ebpf::ST_W_REG
- | ebpf::ST_DW_REG => {
- ebpf::ST_B_IMM | ebpf::ST_B_REG => I8,
- ebpf::ST_H_IMM | ebpf::ST_H_REG => I16,
- ebpf::ST_W_IMM | ebpf::ST_W_REG => I32,
- ebpf::ST_DW_IMM | ebpf::ST_DW_REG => I64,
- let is_imm = match insn.opc {
- ebpf::ST_B_IMM | ebpf::ST_H_IMM | ebpf::ST_W_IMM | ebpf::ST_DW_IMM => true,
- ebpf::ST_B_REG | ebpf::ST_H_REG | ebpf::ST_W_REG | ebpf::ST_DW_REG => false,
- let value = if is_imm {
- self.insn_imm64(bcx, &insn)
- self.insn_src(bcx, &insn)
- let narrow = if ty != I64 {
- bcx.ins().ireduce(ty, value)
- value
- let base = self.insn_dst(bcx, &insn);
- self.reg_store(bcx, ty, base, insn.off, narrow);
- ebpf::ST_W_XADD => unimplemented!(),
- ebpf::ST_DW_XADD => unimplemented!(),
- // BPF_ALU class
- // TODO Check how overflow works in kernel. Should we &= U32MAX all src register value
- // before we do the operation?
- // Cf ((0x11 << 32) - (0x1 << 32)) as u32 VS ((0x11 << 32) as u32 - (0x1 << 32) as u32
- ebpf::ADD32_IMM => {
- let src = self.insn_dst32(bcx, &insn);
- let imm = self.insn_imm32(bcx, &insn);
- let res = bcx.ins().iadd(src, imm);
- self.set_dst32(bcx, &insn, res);
- ebpf::ADD32_REG => {
- //((reg[_dst] & U32MAX) + (reg[_src] & U32MAX)) & U32MAX,
- let lhs = self.insn_dst32(bcx, &insn);
- let rhs = self.insn_src32(bcx, &insn);
- let res = bcx.ins().iadd(lhs, rhs);
- ebpf::SUB32_IMM => {
- // reg[_dst] = (reg[_dst] as i32).wrapping_sub(insn.imm) as u64,
- let res = bcx.ins().isub(src, imm);
- ebpf::SUB32_REG => {
- // reg[_dst] = (reg[_dst] as i32).wrapping_sub(reg[_src] as i32) as u64,
- let res = bcx.ins().isub(lhs, rhs);
- ebpf::MUL32_IMM => {
- // reg[_dst] = (reg[_dst] as i32).wrapping_mul(insn.imm) as u64,
- let res = bcx.ins().imul(src, imm);
- ebpf::MUL32_REG => {
- // reg[_dst] = (reg[_dst] as i32).wrapping_mul(reg[_src] as i32) as u64,
- let res = bcx.ins().imul(lhs, rhs);
- ebpf::DIV32_IMM => {
- // reg[_dst] = (reg[_dst] as u32 / insn.imm as u32) as u64,
- let res = if insn.imm == 0 {
- bcx.ins().iconst(I32, 0)
- bcx.ins().udiv(src, imm)
- ebpf::DIV32_REG => {
- // reg[_dst] = (reg[_dst] as u32 / reg[_src] as u32) as u64,
- let zero = bcx.ins().iconst(I32, 0);
- let one = bcx.ins().iconst(I32, 1);
- let rhs_is_zero = bcx.ins().icmp(IntCC::Equal, rhs, zero);
- let safe_rhs = bcx.ins().select(rhs_is_zero, one, rhs);
- let div_res = bcx.ins().udiv(lhs, safe_rhs);
- let res = bcx.ins().select(rhs_is_zero, zero, div_res);
- ebpf::OR32_IMM => {
- // reg[_dst] = (reg[_dst] as u32 | insn.imm as u32) as u64,
- let res = bcx.ins().bor(src, imm);
- ebpf::OR32_REG => {
- // reg[_dst] = (reg[_dst] as u32 | reg[_src] as u32) as u64,
- let res = bcx.ins().bor(lhs, rhs);
- ebpf::AND32_IMM => {
- // reg[_dst] = (reg[_dst] as u32 & insn.imm as u32) as u64,
- let res = bcx.ins().band(src, imm);
- ebpf::AND32_REG => {
- // reg[_dst] = (reg[_dst] as u32 & reg[_src] as u32) as u64,
- let res = bcx.ins().band(lhs, rhs);
- ebpf::LSH32_IMM => {
- // reg[_dst] = (reg[_dst] as u32).wrapping_shl(insn.imm as u32) as u64,
- let res = bcx.ins().ishl(src, imm);
- ebpf::LSH32_REG => {
- // reg[_dst] = (reg[_dst] as u32).wrapping_shl(reg[_src] as u32) as u64,
- let res = bcx.ins().ishl(lhs, rhs);
- ebpf::RSH32_IMM => {
- // reg[_dst] = (reg[_dst] as u32).wrapping_shr(insn.imm as u32) as u64,
- let res = bcx.ins().ushr(src, imm);
- ebpf::RSH32_REG => {
- // reg[_dst] = (reg[_dst] as u32).wrapping_shr(reg[_src] as u32) as u64,
- let res = bcx.ins().ushr(lhs, rhs);
- ebpf::NEG32 => {
- // { reg[_dst] = (reg[_dst] as i32).wrapping_neg() as u64; reg[_dst] &= U32MAX; },
- let res = bcx.ins().ineg(src);
- // TODO: Do we need to mask the result?
- ebpf::MOD32_IMM => {
- // reg[_dst] = (reg[_dst] as u32 % insn.imm as u32) as u64,
- if insn.imm != 0 {
- let res = bcx.ins().urem(src, imm);
- ebpf::MOD32_REG => {
- // reg[_dst] = (reg[_dst] as u32 % reg[_src] as u32) as u64,
- let div_res = bcx.ins().urem(lhs, safe_rhs);
- let res = bcx.ins().select(rhs_is_zero, lhs, div_res);
- ebpf::XOR32_IMM => {
- // reg[_dst] = (reg[_dst] as u32 ^ insn.imm as u32) as u64,
- let res = bcx.ins().bxor(src, imm);
- ebpf::XOR32_REG => {
- // reg[_dst] = (reg[_dst] as u32 ^ reg[_src] as u32) as u64,
- let res = bcx.ins().bxor(lhs, rhs);
- ebpf::MOV32_IMM => {
- self.set_dst32(bcx, &insn, imm);
- ebpf::MOV32_REG => {
- // reg[_dst] = (reg[_src] as u32) as u64,
- let src = self.insn_src32(bcx, &insn);
- self.set_dst32(bcx, &insn, src);
- ebpf::ARSH32_IMM => {
- // { reg[_dst] = (reg[_dst] as i32).wrapping_shr(insn.imm as u32) as u64; reg[_dst] &= U32MAX; },
- let res = bcx.ins().sshr(src, imm);
- ebpf::ARSH32_REG => {
- // { reg[_dst] = (reg[_dst] as i32).wrapping_shr(reg[_src] as u32) as u64; reg[_dst] &= U32MAX; },
- let res = bcx.ins().sshr(lhs, rhs);
- ebpf::BE | ebpf::LE => {
- let should_swap = match insn.opc {
- ebpf::BE => self.isa.endianness() == Endianness::Little,
- ebpf::LE => self.isa.endianness() == Endianness::Big,
- let ty: Type = match insn.imm {
- 16 => I16,
- 32 => I32,
- 64 => I64,
- if should_swap {
- let src = self.insn_dst(bcx, &insn);
- let src_narrow = if ty != I64 {
- bcx.ins().ireduce(ty, src)
- src
- let res = bcx.ins().bswap(src_narrow);
- let res_wide = if ty != I64 {
- bcx.ins().uextend(I64, res)
- res
- self.set_dst(bcx, &insn, res_wide);
- // BPF_ALU64 class
- ebpf::ADD64_IMM => {
- // reg[_dst] = reg[_dst].wrapping_add(insn.imm as u64),
- let imm = self.insn_imm64(bcx, &insn);
- self.set_dst(bcx, &insn, res);
- ebpf::ADD64_REG => {
- // reg[_dst] = reg[_dst].wrapping_add(reg[_src]),
- let lhs = self.insn_dst(bcx, &insn);
- let rhs = self.insn_src(bcx, &insn);
- ebpf::SUB64_IMM => {
- // reg[_dst] = reg[_dst].wrapping_sub(insn.imm as u64),
- ebpf::SUB64_REG => {
- // reg[_dst] = reg[_dst].wrapping_sub(reg[_src]),
- ebpf::MUL64_IMM => {
- // reg[_dst] = reg[_dst].wrapping_mul(insn.imm as u64),
- ebpf::MUL64_REG => {
- // reg[_dst] = reg[_dst].wrapping_mul(reg[_src]),
- ebpf::DIV64_IMM => {
- // reg[_dst] /= insn.imm as u64,
- bcx.ins().iconst(I64, 0)
- ebpf::DIV64_REG => {
- // reg[_dst] /= reg[_src], if reg[_src] != 0
- // reg[_dst] = 0, if reg[_src] == 0
- let zero = bcx.ins().iconst(I64, 0);
- let one = bcx.ins().iconst(I64, 1);
- ebpf::MOD64_IMM => {
- // reg[_dst] %= insn.imm as u64,
- ebpf::MOD64_REG => {
- // reg[_dst] %= reg[_src], if reg[_src] != 0
- ebpf::OR64_IMM => {
- // reg[_dst] |= insn.imm as u64,
- ebpf::OR64_REG => {
- // reg[_dst] |= reg[_src],
- ebpf::AND64_IMM => {
- // reg[_dst] &= insn.imm as u64,
- ebpf::AND64_REG => {
- // reg[_dst] &= reg[_src],
- ebpf::LSH64_IMM => {
- // reg[_dst] <<= insn.imm as u64,
- ebpf::LSH64_REG => {
- // reg[_dst] <<= reg[_src],
- ebpf::RSH64_IMM => {
- // reg[_dst] >>= insn.imm as u64,
- ebpf::RSH64_REG => {
- // reg[_dst] >>= reg[_src],
- ebpf::NEG64 => {
- // reg[_dst] = -(reg[_dst] as i64) as u64,
- ebpf::XOR64_IMM => {
- // reg[_dst] ^= insn.imm as u64,
- ebpf::XOR64_REG => {
- // reg[_dst] ^= reg[_src],
- ebpf::MOV64_IMM => {
- // reg[_dst] = insn.imm as u64,
- bcx.def_var(self.registers[insn.dst as usize], imm);
- ebpf::MOV64_REG => {
- // reg[_dst] = reg[_src],
- let src = self.insn_src(bcx, &insn);
- bcx.def_var(self.registers[insn.dst as usize], src);
- ebpf::ARSH64_IMM => {
- // reg[_dst] = (reg[_dst] as i64 >> insn.imm) as u64,
- ebpf::ARSH64_REG => {
- // reg[_dst] = (reg[_dst] as i64 >> reg[_src]) as u64,
- // BPF_JMP & BPF_JMP32 class
- ebpf::JA => {
- let (_, target_block) = self.insn_targets[&(insn_ptr as u32)];
- bcx.ins().jump(target_block, &[]);
- ebpf::JEQ_IMM
- | ebpf::JEQ_REG
- | ebpf::JGT_IMM
- | ebpf::JGT_REG
- | ebpf::JGE_IMM
- | ebpf::JGE_REG
- | ebpf::JLT_IMM
- | ebpf::JLT_REG
- | ebpf::JLE_IMM
- | ebpf::JLE_REG
- | ebpf::JNE_IMM
- | ebpf::JNE_REG
- | ebpf::JSGT_IMM
- | ebpf::JSGT_REG
- | ebpf::JSGE_IMM
- | ebpf::JSGE_REG
- | ebpf::JSLT_IMM
- | ebpf::JSLT_REG
- | ebpf::JSLE_IMM
- | ebpf::JSLE_REG
- | ebpf::JSET_IMM
- | ebpf::JSET_REG
- | ebpf::JEQ_IMM32
- | ebpf::JEQ_REG32
- | ebpf::JGT_IMM32
- | ebpf::JGT_REG32
- | ebpf::JGE_IMM32
- | ebpf::JGE_REG32
- | ebpf::JLT_IMM32
- | ebpf::JLT_REG32
- | ebpf::JLE_IMM32
- | ebpf::JLE_REG32
- | ebpf::JNE_IMM32
- | ebpf::JNE_REG32
- | ebpf::JSGT_IMM32
- | ebpf::JSGT_REG32
- | ebpf::JSGE_IMM32
- | ebpf::JSGE_REG32
- | ebpf::JSLT_IMM32
- | ebpf::JSLT_REG32
- | ebpf::JSLE_IMM32
- | ebpf::JSLE_REG32
- | ebpf::JSET_IMM32
- | ebpf::JSET_REG32 => {
- let (fallthrough, target) = self.insn_targets[&(insn_ptr as u32)];
- let is_reg = (insn.opc & BPF_X) != 0;
- let is_32 = (insn.opc & BPF_JMP32) != 0;
- let intcc = match insn.opc {
- c if (c & BPF_ALU_OP_MASK) == BPF_JEQ => IntCC::Equal,
- c if (c & BPF_ALU_OP_MASK) == BPF_JNE => IntCC::NotEqual,
- c if (c & BPF_ALU_OP_MASK) == BPF_JGT => IntCC::UnsignedGreaterThan,
- c if (c & BPF_ALU_OP_MASK) == BPF_JGE => IntCC::UnsignedGreaterThanOrEqual,
- c if (c & BPF_ALU_OP_MASK) == BPF_JLT => IntCC::UnsignedLessThan,
- c if (c & BPF_ALU_OP_MASK) == BPF_JLE => IntCC::UnsignedLessThanOrEqual,
- c if (c & BPF_ALU_OP_MASK) == BPF_JSGT => IntCC::SignedGreaterThan,
- c if (c & BPF_ALU_OP_MASK) == BPF_JSGE => IntCC::SignedGreaterThanOrEqual,
- c if (c & BPF_ALU_OP_MASK) == BPF_JSLT => IntCC::SignedLessThan,
- c if (c & BPF_ALU_OP_MASK) == BPF_JSLE => IntCC::SignedLessThanOrEqual,
- // JSET is handled specially below
- c if (c & BPF_ALU_OP_MASK) == BPF_JSET => IntCC::NotEqual,
- let lhs = if is_32 {
- self.insn_dst32(bcx, &insn)
- self.insn_dst(bcx, &insn)
- let rhs = match (is_reg, is_32) {
- (true, false) => self.insn_src(bcx, &insn),
- (true, true) => self.insn_src32(bcx, &insn),
- (false, false) => self.insn_imm64(bcx, &insn),
- (false, true) => self.insn_imm32(bcx, &insn),
- let cmp_res = if (insn.opc & BPF_ALU_OP_MASK) == BPF_JSET {
- bcx.ins().band(lhs, rhs)
- bcx.ins().icmp(intcc, lhs, rhs)
- bcx.ins().brif(cmp_res, target, &[], fallthrough, &[]);
- // Do not delegate the check to the verifier, since registered functions can be
- // changed after the program has been verified.
- ebpf::CALL => {
- let func_ref = self
- .helper_func_refs
- .get(&(insn.imm as u32))
- .copied()
- .ok_or_else(|| {
- Error::new(
- ErrorKind::Other,
- format!(
- "[CRANELIFT] Error: unknown helper function (id: {:#x})",
- insn.imm as u32
- })?;
- let arg0 = bcx.use_var(self.registers[1]);
- let arg1 = bcx.use_var(self.registers[2]);
- let arg2 = bcx.use_var(self.registers[3]);
- let arg3 = bcx.use_var(self.registers[4]);
- let arg4 = bcx.use_var(self.registers[5]);
- let call = bcx.ins().call(func_ref, &[arg0, arg1, arg2, arg3, arg4]);
- let ret = bcx.inst_results(call)[0];
- self.set_dst(bcx, &insn, ret);
- ebpf::TAIL_CALL => unimplemented!(),
- ebpf::EXIT => {
- let ret = bcx.use_var(self.registers[0]);
- bcx.ins().return_(&[ret]);
- _ => unimplemented!("inst: {:?}", insn),
- fn insn_imm64(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value {
- bcx.ins().iconst(I64, insn.imm as u64 as i64)
- fn insn_imm32(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value {
- bcx.ins().iconst(I32, insn.imm as u32 as u64 as i64)
- fn insn_dst(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value {
- bcx.use_var(self.registers[insn.dst as usize])
- fn insn_dst32(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value {
- let dst = self.insn_dst(bcx, insn);
- bcx.ins().ireduce(I32, dst)
- fn insn_src(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value {
- bcx.use_var(self.registers[insn.src as usize])
- fn insn_src32(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value {
- let src = self.insn_src(bcx, insn);
- bcx.ins().ireduce(I32, src)
- fn set_dst(&mut self, bcx: &mut FunctionBuilder, insn: &Insn, val: Value) {
- bcx.def_var(self.registers[insn.dst as usize], val);
- fn set_dst32(&mut self, bcx: &mut FunctionBuilder, insn: &Insn, val: Value) {
- let val32 = bcx.ins().uextend(I64, val);
- self.set_dst(bcx, insn, val32);
- fn reg_load(&mut self, bcx: &mut FunctionBuilder, ty: Type, base: Value, offset: i16) -> Value {
- self.insert_bounds_check(bcx, ty, base, offset);
- let mut flags = MemFlags::new();
- flags.set_endianness(Endianness::Little);
- bcx.ins().load(ty, flags, base, offset as i32)
- fn reg_store(
- ty: Type,
- base: Value,
- offset: i16,
- val: Value,
- ) {
- bcx.ins().store(flags, val, base, offset as i32);
- /// Inserts a bounds check for a memory access
- ///
- /// This emits a conditional trap if the access is out of bounds for any of the known
- /// valid memory regions. These are the stack, the memory, and the mbuf.
- fn insert_bounds_check(
- let access_size = bcx.ins().iconst(I64, ty.bytes() as i64);
- let offset = bcx.ins().iconst(I64, offset as i64);
- let start_addr = bcx.ins().iadd(base, offset);
- let end_addr = bcx.ins().iadd(start_addr, access_size);
- let does_not_overflow =
- bcx.ins()
- .icmp(IntCC::UnsignedGreaterThanOrEqual, end_addr, start_addr);
- // Check if it's a valid stack access
- let stack_start = bcx.use_var(self.stack_start);
- let stack_end = bcx.use_var(self.stack_end);
- let stack_start_valid =
- .icmp(IntCC::UnsignedGreaterThanOrEqual, start_addr, stack_start);
- let stack_end_valid = bcx
- .icmp(IntCC::UnsignedLessThanOrEqual, end_addr, stack_end);
- let stack_valid = bcx.ins().band(stack_start_valid, stack_end_valid);
- // Check if it's a valid memory access
- let mem_start = bcx.use_var(self.mem_start);
- let mem_end = bcx.use_var(self.mem_end);
- let has_mem = bcx.ins().icmp_imm(IntCC::NotEqual, mem_start, 0);
- let mem_start_valid =
- .icmp(IntCC::UnsignedGreaterThanOrEqual, start_addr, mem_start);
- let mem_end_valid = bcx
- .icmp(IntCC::UnsignedLessThanOrEqual, end_addr, mem_end);
- let mem_valid = bcx.ins().band(mem_start_valid, mem_end_valid);
- let mem_valid = bcx.ins().band(mem_valid, has_mem);
- // Check if it's a valid mbuf access
- let mbuf_start = bcx.use_var(self.mbuf_start);
- let mbuf_end = bcx.use_var(self.mbuf_end);
- let has_mbuf = bcx.ins().icmp_imm(IntCC::NotEqual, mbuf_start, 0);
- let mbuf_start_valid =
- .icmp(IntCC::UnsignedGreaterThanOrEqual, start_addr, mbuf_start);
- let mbuf_end_valid = bcx
- .icmp(IntCC::UnsignedLessThanOrEqual, end_addr, mbuf_end);
- let mbuf_valid = bcx.ins().band(mbuf_start_valid, mbuf_end_valid);
- let mbuf_valid = bcx.ins().band(mbuf_valid, has_mbuf);
- // Join all of these checks together and trap if any of them fails
- // We need it to be valid to at least one region of memory
- let valid_region = bcx.ins().bor(stack_valid, mem_valid);
- let valid_region = bcx.ins().bor(valid_region, mbuf_valid);
- // And that it does not overflow
- let valid = bcx.ins().band(does_not_overflow, valid_region);
- // TODO: We can potentially throw a custom trap code here to indicate
- // which check failed.
- bcx.ins().trapz(valid, TrapCode::HeapOutOfBounds);
- /// Analyze the program and build the CFG
- /// We do this because cranelift does not allow us to switch back to a previously
- /// filled block and add instructions to it. So we can't split the program as we
- /// translate it.
- fn build_cfg(&mut self, bcx: &mut FunctionBuilder, prog: &[u8]) -> Result<(), Error> {
- // This instruction consumes two opcodes
- ebpf::JA
- | ebpf::JEQ_IMM
- | ebpf::JSET_REG32
- | ebpf::EXIT
- | ebpf::TAIL_CALL => {
- self.prepare_jump_blocks(bcx, insn_ptr, &insn);
- _ => {}
- fn prepare_jump_blocks(&mut self, bcx: &mut FunctionBuilder, insn_ptr: usize, insn: &Insn) {
- let insn_ptr = insn_ptr as u32;
- let next_pc: u32 = insn_ptr + 1;
- let target_pc: u32 = (insn_ptr as isize + insn.off as isize + 1)
- // This is the fallthrough block
- let fallthrough_block = *self
- .insn_blocks
- .entry(next_pc)
- .or_insert_with(|| bcx.create_block());
- // Jump Target
- let target_block = *self
- .entry(target_pc)
- // Mark the blocks for this instruction
- self.insn_targets
- .insert(insn_ptr, (fallthrough_block, target_block));
-/// Contains the backing memory for a previously compiled function.
-/// Currently this will allways just contain code for a single function, but
-/// in the future we might want to support multiple functions per module.
-/// Ensures that the backing memory is freed when dropped.
-pub struct CraneliftProgram {
- module: ManuallyDrop<JITModule>,
- main_id: FuncId,
-impl CraneliftProgram {
- pub(crate) fn new(module: JITModule, main_id: FuncId) -> Self {
- module: ManuallyDrop::new(module),
- main_id,
- /// We shouldn't allow this function pointer to be exposed outside of this
- /// module, since it's not guaranteed to be valid after the module is dropped.
- pub(crate) fn get_main_function(&self) -> JittedFunction {
- let function_ptr = self.module.get_finalized_function(self.main_id);
- unsafe { mem::transmute(function_ptr) }
- /// Execute this module by calling the main function
- pub fn execute(
- &self,
- mem_ptr: *mut u8,
- mem_len: usize,
- mbuff_ptr: *mut u8,
- mbuff_len: usize,
- ) -> u64 {
- let main = self.get_main_function();
- main(mem_ptr, mem_len, mbuff_ptr, mbuff_len)
-impl Drop for CraneliftProgram {
- fn drop(&mut self) {
- // We need to have an owned version of `JITModule` to be able to free
- // it's memory. Use `ManuallyDrop` to get the owned `JITModule`.
- // We can no longer use `module` after this, but since we are `Drop`
- // it should be safe.
- let module = ManuallyDrop::take(&mut self.module);
- module.free_memory()
@@ -1,807 +0,0 @@
-//! Functions in this module are used to handle eBPF programs with a higher level representation,
-//! for example to disassemble the code into a human-readable format.
-use log::warn;
-use crate::ebpf;
-#[inline]
-fn alu_imm_str(name: &str, insn: &ebpf::Insn) -> String {
- format!("{name} r{}, {:#x}", insn.dst, insn.imm)
-fn alu_reg_str(name: &str, insn: &ebpf::Insn) -> String {
- format!("{name} r{}, r{}", insn.dst, insn.src)
-fn byteswap_str(name: &str, insn: &ebpf::Insn) -> String {
- match insn.imm {
- 16 | 32 | 64 => {}
- _ => warn!("[Disassembler] Warning: Invalid offset value for {name} insn"),
- format!("{name}{} r{}", insn.imm, insn.dst)
-fn ld_st_imm_str(name: &str, insn: &ebpf::Insn) -> String {
- if insn.off >= 0 {
- format!("{name} [r{}+{:#x}], {:#x}", insn.dst, insn.off, insn.imm)
- "{name} [r{}-{:#x}], {:#x}",
- insn.dst,
- -(insn.off as isize),
- insn.imm
-fn ld_reg_str(name: &str, insn: &ebpf::Insn) -> String {
- format!("{name} r{}, [r{}+{:#x}]", insn.dst, insn.src, insn.off)
- "{name} r{}, [r{}-{:#x}]",
- insn.src,
- -(insn.off as isize)
-fn st_reg_str(name: &str, insn: &ebpf::Insn) -> String {
- format!("{name} [r{}+{:#x}], r{}", insn.dst, insn.off, insn.src)
- "{name} [r{}-{:#x}], r{}",
- insn.src
-fn ldabs_str(name: &str, insn: &ebpf::Insn) -> String {
- format!("{name} {:#x}", insn.imm)
-fn ldind_str(name: &str, insn: &ebpf::Insn) -> String {
- format!("{name} r{}, {:#x}", insn.src, insn.imm)
-fn jmp_imm_str(name: &str, insn: &ebpf::Insn) -> String {
- format!("{name} r{}, {:#x}, +{:#x}", insn.dst, insn.imm, insn.off)
- "{name} r{}, {:#x}, -{:#x}",
- insn.imm,
-fn jmp_reg_str(name: &str, insn: &ebpf::Insn) -> String {
- format!("{name} r{}, r{}, +{:#x}", insn.dst, insn.src, insn.off)
- "{name} r{}, r{}, -{:#x}",
-/// High-level representation of an eBPF instruction.
-/// In addition to standard operation code and various operand, this struct has the following
-/// properties:
-/// * It stores a name, corresponding to a mnemonic for the operation code.
-/// * It also stores a description, which is a mnemonic for the full instruction, using the actual
-/// values of the relevant operands, and that can be used for disassembling the eBPF program for
-/// example.
-/// * Immediate values are stored in an `i64` instead of a traditional i32, in order to merge the
-/// two parts of (otherwise double-length) `LD_DW_IMM` instructions.
-/// See <https://www.kernel.org/doc/Documentation/networking/filter.txt> for the Linux kernel
-/// documentation about eBPF, or <https://github.com/iovisor/bpf-docs/blob/master/eBPF.md> for a
-/// more concise version.
-pub struct HLInsn {
- /// Operation code.
- pub opc: u8,
- /// Name (mnemonic). This name is not canon.
- /// Description of the instruction. This is not canon.
- pub desc: String,
- /// Destination register operand.
- pub dst: u8,
- /// Source register operand.
- pub src: u8,
- /// Offset operand.
- pub off: i16,
- /// Immediate value operand. For `LD_DW_IMM` instructions, contains the whole value merged from
- /// the two 8-bytes parts of the instruction.
- pub imm: i64,
-/// Return a vector of `struct HLInsn` built from an eBPF program.
-/// This is made public to provide a way to manipulate a program as a vector of instructions, in a
-/// high-level format, for example for dumping the program instruction after instruction with a
-/// custom format.
-/// Note that the two parts of `LD_DW_IMM` instructions (that have the size of two standard
-/// instructions) are considered as making a single immediate value. As a consequence, the number
-/// of instructions stored in the vector may not be equal to the size in bytes of the program
-/// divided by the length of an instructions.
-/// To do so, the immediate value operand is stored as an `i64` instead as an i32, so be careful
-/// when you use it (see example `examples/to_json.rs`).
-/// This is to oppose to `ebpf::to_insn_vec()` function, that treats instructions on a low-level
-/// ground and do not merge the parts of `LD_DW_IMM`. Also, the version in `ebpf` module does not
-/// use names or descriptions when storing the instructions.
-/// use rbpf::disassembler;
-/// let prog = &[
-/// 0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55,
-/// 0x00, 0x00, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11,
-/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-/// ];
-/// let v = disassembler::to_insn_vec(prog);
-/// assert_eq!(v, vec![
-/// disassembler::HLInsn {
-/// opc: 0x18,
-/// name: "lddw".to_string(),
-/// desc: "lddw r0, 0x1122334455667788".to_string(),
-/// dst: 0,
-/// src: 0,
-/// off: 0,
-/// imm: 0x1122334455667788
-/// },
-/// opc: 0x95,
-/// name: "exit".to_string(),
-/// desc: "exit".to_string(),
-/// imm: 0
-/// ]);
-pub fn to_insn_vec(prog: &[u8]) -> Vec<HLInsn> {
- if prog.len() % ebpf::INSN_SIZE != 0 {
- panic!(
- "[Disassembler] Error: eBPF program length must be a multiple of {:?} octets",
- ebpf::INSN_SIZE
- if prog.is_empty() {
- return vec![];
- let mut res = vec![];
- let name;
- let desc;
- let mut imm = insn.imm as i64;
- ebpf::LD_ABS_B => {
- name = "ldabsb";
- desc = ldabs_str(name, &insn);
- ebpf::LD_ABS_H => {
- name = "ldabsh";
- ebpf::LD_ABS_W => {
- name = "ldabsw";
- ebpf::LD_ABS_DW => {
- name = "ldabsdw";
- ebpf::LD_IND_B => {
- name = "ldindb";
- desc = ldind_str(name, &insn);
- ebpf::LD_IND_H => {
- name = "ldindh";
- ebpf::LD_IND_W => {
- name = "ldindw";
- ebpf::LD_IND_DW => {
- name = "ldinddw";
- imm = ((insn.imm as u32) as u64 + ((next_insn.imm as u64) << 32)) as i64;
- name = "lddw";
- desc = format!("{name} r{:}, {imm:#x}", insn.dst);
- ebpf::LD_B_REG => {
- name = "ldxb";
- desc = ld_reg_str(name, &insn);
- ebpf::LD_H_REG => {
- name = "ldxh";
- ebpf::LD_W_REG => {
- name = "ldxw";
- ebpf::LD_DW_REG => {
- name = "ldxdw";
- // BPF_ST class
- ebpf::ST_B_IMM => {
- name = "stb";
- desc = ld_st_imm_str(name, &insn);
- ebpf::ST_H_IMM => {
- name = "sth";
- ebpf::ST_W_IMM => {
- name = "stw";
- ebpf::ST_DW_IMM => {
- name = "stdw";
- // BPF_STX class
- ebpf::ST_B_REG => {
- name = "stxb";
- desc = st_reg_str(name, &insn);
- ebpf::ST_H_REG => {
- name = "stxh";
- ebpf::ST_W_REG => {
- name = "stxw";
- ebpf::ST_DW_REG => {
- name = "stxdw";
- ebpf::ST_W_XADD => {
- name = "stxxaddw";
- ebpf::ST_DW_XADD => {
- name = "stxxadddw";
- name = "add32";
- desc = alu_imm_str(name, &insn);
- desc = alu_reg_str(name, &insn);
- name = "sub32";
- name = "mul32";
- name = "div32";
- name = "or32";
- name = "and32";
- name = "lsh32";
- name = "rsh32";
- name = "neg32";
- desc = format!("{name} r{:}", insn.dst);
- name = "mod32";
- name = "xor32";
- name = "mov32";
- name = "arsh32";
- ebpf::LE => {
- name = "le";
- desc = byteswap_str(name, &insn);
- ebpf::BE => {
- name = "be";
- name = "add64";
- name = "sub64";
- name = "mul64";
- name = "div64";
- name = "or64";
- name = "and64";
- name = "lsh64";
- name = "rsh64";
- name = "neg64";
- name = "mod64";
- name = "xor64";
- name = "mov64";
- name = "arsh64";
- // BPF_JMP class
- name = "ja";
- desc = if insn.off >= 0 {
- format!("{name} +{:#x}", insn.off)
- format!("{name} -{:#x}", -insn.off)
- ebpf::JEQ_IMM => {
- name = "jeq";
- desc = jmp_imm_str(name, &insn);
- ebpf::JEQ_REG => {
- desc = jmp_reg_str(name, &insn);
- ebpf::JGT_IMM => {
- name = "jgt";
- ebpf::JGT_REG => {
- ebpf::JGE_IMM => {
- name = "jge";
- ebpf::JGE_REG => {
- ebpf::JLT_IMM => {
- name = "jlt";
- ebpf::JLT_REG => {
- ebpf::JLE_IMM => {
- name = "jle";
- ebpf::JLE_REG => {
- ebpf::JSET_IMM => {
- name = "jset";
- ebpf::JSET_REG => {
- ebpf::JNE_IMM => {
- name = "jne";
- ebpf::JNE_REG => {
- ebpf::JSGT_IMM => {
- name = "jsgt";
- ebpf::JSGT_REG => {
- ebpf::JSGE_IMM => {
- name = "jsge";
- ebpf::JSGE_REG => {
- ebpf::JSLT_IMM => {
- name = "jslt";
- ebpf::JSLT_REG => {
- ebpf::JSLE_IMM => {
- name = "jsle";
- ebpf::JSLE_REG => {
- name = "call";
- desc = format!("{name} {:#x}", insn.imm);
- ebpf::TAIL_CALL => {
- name = "tail_call";
- desc = name.to_string();
- name = "exit";
- // BPF_JMP32 class
- ebpf::JEQ_IMM32 => {
- name = "jeq32";
- ebpf::JEQ_REG32 => {
- ebpf::JGT_IMM32 => {
- name = "jgt32";
- ebpf::JGT_REG32 => {
- ebpf::JGE_IMM32 => {
- name = "jge32";
- ebpf::JGE_REG32 => {
- ebpf::JLT_IMM32 => {
- name = "jlt32";
- ebpf::JLT_REG32 => {
- ebpf::JLE_IMM32 => {
- name = "jle32";
- ebpf::JLE_REG32 => {
- ebpf::JSET_IMM32 => {
- name = "jset32";
- ebpf::JSET_REG32 => {
- ebpf::JNE_IMM32 => {
- name = "jne32";
- ebpf::JNE_REG32 => {
- ebpf::JSGT_IMM32 => {
- name = "jsgt32";
- ebpf::JSGT_REG32 => {
- ebpf::JSGE_IMM32 => {
- name = "jsge32";
- ebpf::JSGE_REG32 => {
- ebpf::JSLT_IMM32 => {
- name = "jslt32";
- ebpf::JSLT_REG32 => {
- ebpf::JSLE_IMM32 => {
- name = "jsle32";
- ebpf::JSLE_REG32 => {
- _ => {
- "[Disassembler] Error: unknown eBPF opcode {:#2x} (insn #{:?})",
- insn.opc, insn_ptr
- let hl_insn = HLInsn {
- opc: insn.opc,
- name: name.to_string(),
- desc,
- dst: insn.dst,
- src: insn.src,
- off: insn.off,
- imm,
- res.push(hl_insn);
-/// Disassemble an eBPF program into human-readable instructions and prints it to standard output.
-/// The program is not checked for errors or inconsistencies.
-/// 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
-/// disassembler::disassemble(prog);
-/// # // "\nadd64 r1, 0x605\nmov64 r2, 0x32\nmov64 r1, r0\nbe16 r0\nneg64 r2\nexit"
-/// add64 r1, 0x605
-/// exit
-pub fn disassemble(prog: &[u8]) {
- for insn in to_insn_vec(prog) {
- println!("{}", insn.desc);
- log::info!("{}", insn.desc);
@@ -1,635 +0,0 @@
-//! This module contains all the definitions related to eBPF, and some functions permitting to
-//! manipulate eBPF instructions.
-//!
-//! The number of bytes in an instruction, the maximum number of instructions in a program, and
-//! also all operation codes are defined here as constants.
-//! The structure for an instruction used by this crate, as well as the function to extract it from
-//! a program, is also defined in the module.
-//! To learn more about these instructions, see the Linux kernel documentation:
-//! <https://www.kernel.org/doc/Documentation/networking/filter.txt>, or for a shorter version of
-//! the list of the operation codes: <https://github.com/iovisor/bpf-docs/blob/master/eBPF.md>
-use alloc::{vec, vec::Vec};
-use byteorder::{ByteOrder, LittleEndian};
-/// The maximum call depth is 8
-pub const RBPF_MAX_CALL_DEPTH: usize = 8;
-/// Maximum number of instructions in an eBPF program.
-pub const PROG_MAX_INSNS: usize = 1000000;
-/// Size of an eBPF instructions, in bytes.
-pub const INSN_SIZE: usize = 8;
-/// Maximum size of an eBPF program, in bytes.
-pub const PROG_MAX_SIZE: usize = PROG_MAX_INSNS * INSN_SIZE;
-/// Stack for the eBPF stack, in bytes.
-pub const STACK_SIZE: usize = 512;
-// eBPF op codes.
-// See also https://www.kernel.org/doc/Documentation/networking/filter.txt
-// Three least significant bits are operation class:
-/// BPF operation class: load from immediate.
-pub const BPF_LD: u8 = 0x00;
-/// BPF operation class: load from register.
-pub const BPF_LDX: u8 = 0x01;
-/// BPF operation class: store immediate.
-pub const BPF_ST: u8 = 0x02;
-/// BPF operation class: store value from register.
-pub const BPF_STX: u8 = 0x03;
-/// BPF operation class: 32 bits arithmetic operation.
-pub const BPF_ALU: u8 = 0x04;
-/// BPF operation class: jump (64-bit wide operands for comparisons).
-pub const BPF_JMP: u8 = 0x05;
-/// BPF operation class: jump (32-bit wide operands for comparisons).
-pub const BPF_JMP32: u8 = 0x06;
-// [ class 6 unused, reserved for future use ]
-/// BPF operation class: 64 bits arithmetic operation.
-pub const BPF_ALU64: u8 = 0x07;
-// For load and store instructions:
-// +------------+--------+------------+
-// | 3 bits | 2 bits | 3 bits |
-// | mode | size | insn class |
-// (MSB) (LSB)
-// Size modifiers:
-/// BPF size modifier: word (4 bytes).
-pub const BPF_W: u8 = 0x00;
-/// BPF size modifier: half-word (2 bytes).
-pub const BPF_H: u8 = 0x08;
-/// BPF size modifier: byte (1 byte).
-pub const BPF_B: u8 = 0x10;
-/// BPF size modifier: double word (8 bytes).
-pub const BPF_DW: u8 = 0x18;
-// Mode modifiers:
-/// BPF mode modifier: immediate value.
-pub const BPF_IMM: u8 = 0x00;
-/// BPF mode modifier: absolute load.
-pub const BPF_ABS: u8 = 0x20;
-/// BPF mode modifier: indirect load.
-pub const BPF_IND: u8 = 0x40;
-/// BPF mode modifier: load from / store to memory.
-pub const BPF_MEM: u8 = 0x60;
-// [ 0x80 reserved ]
-// [ 0xa0 reserved ]
-/// BPF mode modifier: exclusive add.
-pub const BPF_XADD: u8 = 0xc0;
-// For arithmetic (BPF_ALU/BPF_ALU64) and jump (BPF_JMP) instructions:
-// +----------------+--------+--------+
-// | 4 bits |1 b.| 3 bits |
-// | operation code | src| insn class |
-// +----------------+----+------------+
-// Source modifiers:
-/// BPF source operand modifier: 32-bit immediate value.
-pub const BPF_K: u8 = 0x00;
-/// BPF source operand modifier: `src` register.
-pub const BPF_X: u8 = 0x08;
-// Operation codes -- BPF_ALU or BPF_ALU64 classes:
-/// BPF ALU/ALU64 operation code: addition.
-pub const BPF_ADD: u8 = 0x00;
-/// BPF ALU/ALU64 operation code: subtraction.
-pub const BPF_SUB: u8 = 0x10;
-/// BPF ALU/ALU64 operation code: multiplication.
-pub const BPF_MUL: u8 = 0x20;
-/// BPF ALU/ALU64 operation code: division.
-pub const BPF_DIV: u8 = 0x30;
-/// BPF ALU/ALU64 operation code: or.
-pub const BPF_OR: u8 = 0x40;
-/// BPF ALU/ALU64 operation code: and.
-pub const BPF_AND: u8 = 0x50;
-/// BPF ALU/ALU64 operation code: left shift.
-pub const BPF_LSH: u8 = 0x60;
-/// BPF ALU/ALU64 operation code: right shift.
-pub const BPF_RSH: u8 = 0x70;
-/// BPF ALU/ALU64 operation code: negation.
-pub const BPF_NEG: u8 = 0x80;
-/// BPF ALU/ALU64 operation code: modulus.
-pub const BPF_MOD: u8 = 0x90;
-/// BPF ALU/ALU64 operation code: exclusive or.
-pub const BPF_XOR: u8 = 0xa0;
-/// BPF ALU/ALU64 operation code: move.
-pub const BPF_MOV: u8 = 0xb0;
-/// BPF ALU/ALU64 operation code: sign extending right shift.
-pub const BPF_ARSH: u8 = 0xc0;
-/// BPF ALU/ALU64 operation code: endianness conversion.
-pub const BPF_END: u8 = 0xd0;
-// Operation codes -- BPF_JMP or BPF_JMP32 classes:
-/// BPF JMP operation code: jump.
-pub const BPF_JA: u8 = 0x00;
-/// BPF JMP operation code: jump if equal.
-pub const BPF_JEQ: u8 = 0x10;
-/// BPF JMP operation code: jump if greater than.
-pub const BPF_JGT: u8 = 0x20;
-/// BPF JMP operation code: jump if greater or equal.
-pub const BPF_JGE: u8 = 0x30;
-/// BPF JMP operation code: jump if `src` & `reg`.
-pub const BPF_JSET: u8 = 0x40;
-/// BPF JMP operation code: jump if not equal.
-pub const BPF_JNE: u8 = 0x50;
-/// BPF JMP operation code: jump if greater than (signed).
-pub const BPF_JSGT: u8 = 0x60;
-/// BPF JMP operation code: jump if greater or equal (signed).
-pub const BPF_JSGE: u8 = 0x70;
-/// BPF JMP operation code: helper function call.
-pub const BPF_CALL: u8 = 0x80;
-/// BPF JMP operation code: return from program.
-pub const BPF_EXIT: u8 = 0x90;
-/// BPF JMP operation code: jump if lower than.
-pub const BPF_JLT: u8 = 0xa0;
-/// BPF JMP operation code: jump if lower or equal.
-pub const BPF_JLE: u8 = 0xb0;
-/// BPF JMP operation code: jump if lower than (signed).
-pub const BPF_JSLT: u8 = 0xc0;
-/// BPF JMP operation code: jump if lower or equal (signed).
-pub const BPF_JSLE: u8 = 0xd0;
-// Op codes
-// (Following operation names are not “official”, but may be proper to rbpf; Linux kernel only
-// combines above flags and does not attribute a name per operation.)
-/// BPF opcode: `ldabsb src, dst, imm`.
-pub const LD_ABS_B: u8 = BPF_LD | BPF_ABS | BPF_B;
-/// BPF opcode: `ldabsh src, dst, imm`.
-pub const LD_ABS_H: u8 = BPF_LD | BPF_ABS | BPF_H;
-/// BPF opcode: `ldabsw src, dst, imm`.
-pub const LD_ABS_W: u8 = BPF_LD | BPF_ABS | BPF_W;
-/// BPF opcode: `ldabsdw src, dst, imm`.
-pub const LD_ABS_DW: u8 = BPF_LD | BPF_ABS | BPF_DW;
-/// BPF opcode: `ldindb src, dst, imm`.
-pub const LD_IND_B: u8 = BPF_LD | BPF_IND | BPF_B;
-/// BPF opcode: `ldindh src, dst, imm`.
-pub const LD_IND_H: u8 = BPF_LD | BPF_IND | BPF_H;
-/// BPF opcode: `ldindw src, dst, imm`.
-pub const LD_IND_W: u8 = BPF_LD | BPF_IND | BPF_W;
-/// BPF opcode: `ldinddw src, dst, imm`.
-pub const LD_IND_DW: u8 = BPF_LD | BPF_IND | BPF_DW;
-#[allow(unknown_lints)]
-#[allow(clippy::eq_op)]
-/// BPF opcode: `lddw dst, imm` /// `dst = imm`.
-pub const LD_DW_IMM: u8 = BPF_LD | BPF_IMM | BPF_DW;
-/// BPF opcode: `ldxb dst, [src + off]` /// `dst = (src + off) as u8`.
-pub const LD_B_REG: u8 = BPF_LDX | BPF_MEM | BPF_B;
-/// BPF opcode: `ldxh dst, [src + off]` /// `dst = (src + off) as u16`.
-pub const LD_H_REG: u8 = BPF_LDX | BPF_MEM | BPF_H;
-/// BPF opcode: `ldxw dst, [src + off]` /// `dst = (src + off) as u32`.
-pub const LD_W_REG: u8 = BPF_LDX | BPF_MEM | BPF_W;
-/// BPF opcode: `ldxdw dst, [src + off]` /// `dst = (src + off) as u64`.
-pub const LD_DW_REG: u8 = BPF_LDX | BPF_MEM | BPF_DW;
-/// BPF opcode: `stb [dst + off], imm` /// `(dst + offset) as u8 = imm`.
-pub const ST_B_IMM: u8 = BPF_ST | BPF_MEM | BPF_B;
-/// BPF opcode: `sth [dst + off], imm` /// `(dst + offset) as u16 = imm`.
-pub const ST_H_IMM: u8 = BPF_ST | BPF_MEM | BPF_H;
-/// BPF opcode: `stw [dst + off], imm` /// `(dst + offset) as u32 = imm`.
-pub const ST_W_IMM: u8 = BPF_ST | BPF_MEM | BPF_W;
-/// BPF opcode: `stdw [dst + off], imm` /// `(dst + offset) as u64 = imm`.
-pub const ST_DW_IMM: u8 = BPF_ST | BPF_MEM | BPF_DW;
-/// BPF opcode: `stxb [dst + off], src` /// `(dst + offset) as u8 = src`.
-pub const ST_B_REG: u8 = BPF_STX | BPF_MEM | BPF_B;
-/// BPF opcode: `stxh [dst + off], src` /// `(dst + offset) as u16 = src`.
-pub const ST_H_REG: u8 = BPF_STX | BPF_MEM | BPF_H;
-/// BPF opcode: `stxw [dst + off], src` /// `(dst + offset) as u32 = src`.
-pub const ST_W_REG: u8 = BPF_STX | BPF_MEM | BPF_W;
-/// BPF opcode: `stxdw [dst + off], src` /// `(dst + offset) as u64 = src`.
-pub const ST_DW_REG: u8 = BPF_STX | BPF_MEM | BPF_DW;
-/// BPF opcode: `stxxaddw [dst + off], src`.
-pub const ST_W_XADD: u8 = BPF_STX | BPF_XADD | BPF_W;
-/// BPF opcode: `stxxadddw [dst + off], src`.
-pub const ST_DW_XADD: u8 = BPF_STX | BPF_XADD | BPF_DW;
-/// BPF opcode: `add32 dst, imm` /// `dst += imm`.
-pub const ADD32_IMM: u8 = BPF_ALU | BPF_K | BPF_ADD;
-/// BPF opcode: `add32 dst, src` /// `dst += src`.
-pub const ADD32_REG: u8 = BPF_ALU | BPF_X | BPF_ADD;
-/// BPF opcode: `sub32 dst, imm` /// `dst -= imm`.
-pub const SUB32_IMM: u8 = BPF_ALU | BPF_K | BPF_SUB;
-/// BPF opcode: `sub32 dst, src` /// `dst -= src`.
-pub const SUB32_REG: u8 = BPF_ALU | BPF_X | BPF_SUB;
-/// BPF opcode: `mul32 dst, imm` /// `dst *= imm`.
-pub const MUL32_IMM: u8 = BPF_ALU | BPF_K | BPF_MUL;
-/// BPF opcode: `mul32 dst, src` /// `dst *= src`.
-pub const MUL32_REG: u8 = BPF_ALU | BPF_X | BPF_MUL;
-/// BPF opcode: `div32 dst, imm` /// `dst /= imm`.
-pub const DIV32_IMM: u8 = BPF_ALU | BPF_K | BPF_DIV;
-/// BPF opcode: `div32 dst, src` /// `dst /= src`.
-pub const DIV32_REG: u8 = BPF_ALU | BPF_X | BPF_DIV;
-/// BPF opcode: `or32 dst, imm` /// `dst |= imm`.
-pub const OR32_IMM: u8 = BPF_ALU | BPF_K | BPF_OR;
-/// BPF opcode: `or32 dst, src` /// `dst |= src`.
-pub const OR32_REG: u8 = BPF_ALU | BPF_X | BPF_OR;
-/// BPF opcode: `and32 dst, imm` /// `dst &= imm`.
-pub const AND32_IMM: u8 = BPF_ALU | BPF_K | BPF_AND;
-/// BPF opcode: `and32 dst, src` /// `dst &= src`.
-pub const AND32_REG: u8 = BPF_ALU | BPF_X | BPF_AND;
-/// BPF opcode: `lsh32 dst, imm` /// `dst <<= imm`.
-pub const LSH32_IMM: u8 = BPF_ALU | BPF_K | BPF_LSH;
-/// BPF opcode: `lsh32 dst, src` /// `dst <<= src`.
-pub const LSH32_REG: u8 = BPF_ALU | BPF_X | BPF_LSH;
-/// BPF opcode: `rsh32 dst, imm` /// `dst >>= imm`.
-pub const RSH32_IMM: u8 = BPF_ALU | BPF_K | BPF_RSH;
-/// BPF opcode: `rsh32 dst, src` /// `dst >>= src`.
-pub const RSH32_REG: u8 = BPF_ALU | BPF_X | BPF_RSH;
-/// BPF opcode: `neg32 dst` /// `dst = -dst`.
-pub const NEG32: u8 = BPF_ALU | BPF_NEG;
-/// BPF opcode: `mod32 dst, imm` /// `dst %= imm`.
-pub const MOD32_IMM: u8 = BPF_ALU | BPF_K | BPF_MOD;
-/// BPF opcode: `mod32 dst, src` /// `dst %= src`.
-pub const MOD32_REG: u8 = BPF_ALU | BPF_X | BPF_MOD;
-/// BPF opcode: `xor32 dst, imm` /// `dst ^= imm`.
-pub const XOR32_IMM: u8 = BPF_ALU | BPF_K | BPF_XOR;
-/// BPF opcode: `xor32 dst, src` /// `dst ^= src`.
-pub const XOR32_REG: u8 = BPF_ALU | BPF_X | BPF_XOR;
-/// BPF opcode: `mov32 dst, imm` /// `dst = imm`.
-pub const MOV32_IMM: u8 = BPF_ALU | BPF_K | BPF_MOV;
-/// BPF opcode: `mov32 dst, src` /// `dst = src`.
-pub const MOV32_REG: u8 = BPF_ALU | BPF_X | BPF_MOV;
-/// BPF opcode: `arsh32 dst, imm` /// `dst >>= imm (arithmetic)`.
-/// <https://en.wikipedia.org/wiki/Arithmetic_shift>
-pub const ARSH32_IMM: u8 = BPF_ALU | BPF_K | BPF_ARSH;
-/// BPF opcode: `arsh32 dst, src` /// `dst >>= src (arithmetic)`.
-pub const ARSH32_REG: u8 = BPF_ALU | BPF_X | BPF_ARSH;
-/// BPF opcode: `le dst` /// `dst = htole<imm>(dst), with imm in {16, 32, 64}`.
-pub const LE: u8 = BPF_ALU | BPF_K | BPF_END;
-/// BPF opcode: `be dst` /// `dst = htobe<imm>(dst), with imm in {16, 32, 64}`.
-pub const BE: u8 = BPF_ALU | BPF_X | BPF_END;
-/// BPF opcode: `add64 dst, imm` /// `dst += imm`.
-pub const ADD64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_ADD;
-/// BPF opcode: `add64 dst, src` /// `dst += src`.
-pub const ADD64_REG: u8 = BPF_ALU64 | BPF_X | BPF_ADD;
-/// BPF opcode: `sub64 dst, imm` /// `dst -= imm`.
-pub const SUB64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_SUB;
-/// BPF opcode: `sub64 dst, src` /// `dst -= src`.
-pub const SUB64_REG: u8 = BPF_ALU64 | BPF_X | BPF_SUB;
-/// BPF opcode: `div64 dst, imm` /// `dst /= imm`.
-pub const MUL64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_MUL;
-/// BPF opcode: `div64 dst, src` /// `dst /= src`.
-pub const MUL64_REG: u8 = BPF_ALU64 | BPF_X | BPF_MUL;
-pub const DIV64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_DIV;
-pub const DIV64_REG: u8 = BPF_ALU64 | BPF_X | BPF_DIV;
-/// BPF opcode: `or64 dst, imm` /// `dst |= imm`.
-pub const OR64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_OR;
-/// BPF opcode: `or64 dst, src` /// `dst |= src`.
-pub const OR64_REG: u8 = BPF_ALU64 | BPF_X | BPF_OR;
-/// BPF opcode: `and64 dst, imm` /// `dst &= imm`.
-pub const AND64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_AND;
-/// BPF opcode: `and64 dst, src` /// `dst &= src`.
-pub const AND64_REG: u8 = BPF_ALU64 | BPF_X | BPF_AND;
-/// BPF opcode: `lsh64 dst, imm` /// `dst <<= imm`.
-pub const LSH64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_LSH;
-/// BPF opcode: `lsh64 dst, src` /// `dst <<= src`.
-pub const LSH64_REG: u8 = BPF_ALU64 | BPF_X | BPF_LSH;
-/// BPF opcode: `rsh64 dst, imm` /// `dst >>= imm`.
-pub const RSH64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_RSH;
-/// BPF opcode: `rsh64 dst, src` /// `dst >>= src`.
-pub const RSH64_REG: u8 = BPF_ALU64 | BPF_X | BPF_RSH;
-/// BPF opcode: `neg64 dst, imm` /// `dst = -dst`.
-pub const NEG64: u8 = BPF_ALU64 | BPF_NEG;
-/// BPF opcode: `mod64 dst, imm` /// `dst %= imm`.
-pub const MOD64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_MOD;
-/// BPF opcode: `mod64 dst, src` /// `dst %= src`.
-pub const MOD64_REG: u8 = BPF_ALU64 | BPF_X | BPF_MOD;
-/// BPF opcode: `xor64 dst, imm` /// `dst ^= imm`.
-pub const XOR64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_XOR;
-/// BPF opcode: `xor64 dst, src` /// `dst ^= src`.
-pub const XOR64_REG: u8 = BPF_ALU64 | BPF_X | BPF_XOR;
-/// BPF opcode: `mov64 dst, imm` /// `dst = imm`.
-pub const MOV64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_MOV;
-/// BPF opcode: `mov64 dst, src` /// `dst = src`.
-pub const MOV64_REG: u8 = BPF_ALU64 | BPF_X | BPF_MOV;
-/// BPF opcode: `arsh64 dst, imm` /// `dst >>= imm (arithmetic)`.
-pub const ARSH64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_ARSH;
-/// BPF opcode: `arsh64 dst, src` /// `dst >>= src (arithmetic)`.
-pub const ARSH64_REG: u8 = BPF_ALU64 | BPF_X | BPF_ARSH;
-/// BPF opcode: `ja +off` /// `PC += off`.
-pub const JA: u8 = BPF_JMP | BPF_JA;
-/// BPF opcode: `jeq dst, imm, +off` /// `PC += off if dst == imm`.
-pub const JEQ_IMM: u8 = BPF_JMP | BPF_K | BPF_JEQ;
-/// BPF opcode: `jeq dst, src, +off` /// `PC += off if dst == src`.
-pub const JEQ_REG: u8 = BPF_JMP | BPF_X | BPF_JEQ;
-/// BPF opcode: `jgt dst, imm, +off` /// `PC += off if dst > imm`.
-pub const JGT_IMM: u8 = BPF_JMP | BPF_K | BPF_JGT;
-/// BPF opcode: `jgt dst, src, +off` /// `PC += off if dst > src`.
-pub const JGT_REG: u8 = BPF_JMP | BPF_X | BPF_JGT;
-/// BPF opcode: `jge dst, imm, +off` /// `PC += off if dst >= imm`.
-pub const JGE_IMM: u8 = BPF_JMP | BPF_K | BPF_JGE;
-/// BPF opcode: `jge dst, src, +off` /// `PC += off if dst >= src`.
-pub const JGE_REG: u8 = BPF_JMP | BPF_X | BPF_JGE;
-/// BPF opcode: `jlt dst, imm, +off` /// `PC += off if dst < imm`.
-pub const JLT_IMM: u8 = BPF_JMP | BPF_K | BPF_JLT;
-/// BPF opcode: `jlt dst, src, +off` /// `PC += off if dst < src`.
-pub const JLT_REG: u8 = BPF_JMP | BPF_X | BPF_JLT;
-/// BPF opcode: `jle dst, imm, +off` /// `PC += off if dst <= imm`.
-pub const JLE_IMM: u8 = BPF_JMP | BPF_K | BPF_JLE;
-/// BPF opcode: `jle dst, src, +off` /// `PC += off if dst <= src`.
-pub const JLE_REG: u8 = BPF_JMP | BPF_X | BPF_JLE;
-/// BPF opcode: `jset dst, imm, +off` /// `PC += off if dst & imm`.
-pub const JSET_IMM: u8 = BPF_JMP | BPF_K | BPF_JSET;
-/// BPF opcode: `jset dst, src, +off` /// `PC += off if dst & src`.
-pub const JSET_REG: u8 = BPF_JMP | BPF_X | BPF_JSET;
-/// BPF opcode: `jne dst, imm, +off` /// `PC += off if dst != imm`.
-pub const JNE_IMM: u8 = BPF_JMP | BPF_K | BPF_JNE;
-/// BPF opcode: `jne dst, src, +off` /// `PC += off if dst != src`.
-pub const JNE_REG: u8 = BPF_JMP | BPF_X | BPF_JNE;
-/// BPF opcode: `jsgt dst, imm, +off` /// `PC += off if dst > imm (signed)`.
-pub const JSGT_IMM: u8 = BPF_JMP | BPF_K | BPF_JSGT;
-/// BPF opcode: `jsgt dst, src, +off` /// `PC += off if dst > src (signed)`.
-pub const JSGT_REG: u8 = BPF_JMP | BPF_X | BPF_JSGT;
-/// BPF opcode: `jsge dst, imm, +off` /// `PC += off if dst >= imm (signed)`.
-pub const JSGE_IMM: u8 = BPF_JMP | BPF_K | BPF_JSGE;
-/// BPF opcode: `jsge dst, src, +off` /// `PC += off if dst >= src (signed)`.
-pub const JSGE_REG: u8 = BPF_JMP | BPF_X | BPF_JSGE;
-/// BPF opcode: `jslt dst, imm, +off` /// `PC += off if dst < imm (signed)`.
-pub const JSLT_IMM: u8 = BPF_JMP | BPF_K | BPF_JSLT;
-/// BPF opcode: `jslt dst, src, +off` /// `PC += off if dst < src (signed)`.
-pub const JSLT_REG: u8 = BPF_JMP | BPF_X | BPF_JSLT;
-/// BPF opcode: `jsle dst, imm, +off` /// `PC += off if dst <= imm (signed)`.
-pub const JSLE_IMM: u8 = BPF_JMP | BPF_K | BPF_JSLE;
-/// BPF opcode: `jsle dst, src, +off` /// `PC += off if dst <= src (signed)`.
-pub const JSLE_REG: u8 = BPF_JMP | BPF_X | BPF_JSLE;
-/// BPF opcode: `jeq dst, imm, +off` /// `PC += off if (dst as u32) == imm`.
-pub const JEQ_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JEQ;
-/// BPF opcode: `jeq dst, src, +off` /// `PC += off if (dst as u32) == (src as u32)`.
-pub const JEQ_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JEQ;
-/// BPF opcode: `jgt dst, imm, +off` /// `PC += off if (dst as u32) > imm`.
-pub const JGT_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JGT;
-/// BPF opcode: `jgt dst, src, +off` /// `PC += off if (dst as u32) > (src as u32)`.
-pub const JGT_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JGT;
-/// BPF opcode: `jge dst, imm, +off` /// `PC += off if (dst as u32) >= imm`.
-pub const JGE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JGE;
-/// BPF opcode: `jge dst, src, +off` /// `PC += off if (dst as u32) >= (src as u32)`.
-pub const JGE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JGE;
-/// BPF opcode: `jlt dst, imm, +off` /// `PC += off if (dst as u32) < imm`.
-pub const JLT_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JLT;
-/// BPF opcode: `jlt dst, src, +off` /// `PC += off if (dst as u32) < (src as u32)`.
-pub const JLT_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JLT;
-/// BPF opcode: `jle dst, imm, +off` /// `PC += off if (dst as u32) <= imm`.
-pub const JLE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JLE;
-/// BPF opcode: `jle dst, src, +off` /// `PC += off if (dst as u32) <= (src as u32)`.
-pub const JLE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JLE;
-/// BPF opcode: `jset dst, imm, +off` /// `PC += off if (dst as u32) & imm`.
-pub const JSET_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSET;
-/// BPF opcode: `jset dst, src, +off` /// `PC += off if (dst as u32) & (src as u32)`.
-pub const JSET_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSET;
-/// BPF opcode: `jne dst, imm, +off` /// `PC += off if (dst as u32) != imm`.
-pub const JNE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JNE;
-/// BPF opcode: `jne dst, src, +off` /// `PC += off if (dst as u32) != (src as u32)`.
-pub const JNE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JNE;
-/// BPF opcode: `jsgt dst, imm, +off` /// `PC += off if (dst as i32) > imm (signed)`.
-pub const JSGT_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSGT;
-/// BPF opcode: `jsgt dst, src, +off` /// `PC += off if (dst as i32) > (src as i32) (signed)`.
-pub const JSGT_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSGT;
-/// BPF opcode: `jsge dst, imm, +off` /// `PC += off if (dst as i32) >= imm (signed)`.
-pub const JSGE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSGE;
-/// BPF opcode: `jsge dst, src, +off` /// `PC += off if (dst as i32) >= (src as i32) (signed)`.
-pub const JSGE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSGE;
-/// BPF opcode: `jslt dst, imm, +off` /// `PC += off if (dst as i32) < imm (signed)`.
-pub const JSLT_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSLT;
-/// BPF opcode: `jslt dst, src, +off` /// `PC += off if (dst as i32) < (src as i32) (signed)`.
-pub const JSLT_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSLT;
-/// BPF opcode: `jsle dst, imm, +off` /// `PC += off if (dst as i32) <= imm (signed)`.
-pub const JSLE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSLE;
-/// BPF opcode: `jsle dst, src, +off` /// `PC += off if (dst as i32) <= (src as i32) (signed)`.
-pub const JSLE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSLE;
-/// BPF opcode: `call imm` /// helper function call to helper with key `imm`.
-pub const CALL: u8 = BPF_JMP | BPF_CALL;
-/// BPF opcode: tail call.
-pub const TAIL_CALL: u8 = BPF_JMP | BPF_X | BPF_CALL;
-/// BPF opcode: `exit` /// `return r0`.
-pub const EXIT: u8 = BPF_JMP | BPF_EXIT;
-// Used in JIT
-/// Mask to extract the operation class from an operation code.
-pub const BPF_CLS_MASK: u8 = 0x07;
-/// Mask to extract the arithmetic operation code from an instruction operation code.
-pub const BPF_ALU_OP_MASK: u8 = 0xf0;
-/// Prototype of an eBPF helper function.
-pub type Helper = fn(u64, u64, u64, u64, u64) -> u64;
-/// An eBPF instruction.
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Insn {
- /// Immediate value operand.
- pub imm: i32,
-impl Insn {
- /// Turn an `Insn` back into an array of bytes.
- /// # Examples
- /// ```
- /// use rbpf::ebpf;
- /// let prog: &[u8] = &[
- /// 0xb7, 0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78,
- /// ];
- /// let insn = ebpf::Insn {
- /// opc: 0xb7,
- /// dst: 2,
- /// src: 1,
- /// off: 0x3456,
- /// imm: 0x789abcde
- /// };
- /// assert_eq!(insn.to_array(), prog);
- pub fn to_array(&self) -> [u8; INSN_SIZE] {
- [
- self.opc,
- self.src.wrapping_shl(4) | self.dst,
- (self.off & 0xff) as u8,
- self.off.wrapping_shr(8) as u8,
- (self.imm & 0xff) as u8,
- (self.imm & 0xff_00).wrapping_shr(8) as u8,
- (self.imm as u32 & 0xff_00_00).wrapping_shr(16) as u8,
- (self.imm as u32 & 0xff_00_00_00).wrapping_shr(24) as u8,
- ]
- /// Turn an `Insn` into an vector of bytes.
- /// let prog: Vec<u8> = vec![
- /// assert_eq!(insn.to_vec(), prog);
- pub fn to_vec(&self) -> Vec<u8> {
- vec![
-/// Get the instruction at `idx` of an eBPF program. `idx` is the index (number) of the
-/// instruction (not a byte offset). The first instruction has index 0.
-/// # Panics
-/// Panics if it is not possible to get the instruction (if idx is too high, or last instruction is
-/// incomplete).
-/// use rbpf::ebpf;
-/// 0xb7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-/// let insn = ebpf::get_insn(prog, 1);
-/// assert_eq!(insn.opc, 0x95);
-/// The example below will panic, since the last instruction is not complete and cannot be loaded.
-/// ```rust,should_panic
-/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00 // two bytes missing
-pub fn get_insn(prog: &[u8], idx: usize) -> Insn {
- // This guard should not be needed in most cases, since the verifier already checks the program
- // size, and indexes should be fine in the interpreter/JIT. But this function is publicly
- // available and user can call it with any `idx`, so we have to check anyway.
- if (idx + 1) * INSN_SIZE > prog.len() {
- "Error: cannot reach instruction at index {:?} in program containing {:?} bytes",
- idx,
- prog.len()
- Insn {
- opc: prog[INSN_SIZE * idx],
- dst: prog[INSN_SIZE * idx + 1] & 0x0f,
- src: (prog[INSN_SIZE * idx + 1] & 0xf0) >> 4,
- off: LittleEndian::read_i16(&prog[(INSN_SIZE * idx + 2)..]),
- imm: LittleEndian::read_i32(&prog[(INSN_SIZE * idx + 4)..]),
-/// Return a vector of `struct Insn` built from a program.
-/// This is provided as a convenience for users wishing to manipulate a vector of instructions, for
-/// example for dumping the program instruction after instruction with a custom format.
-/// Note that the two parts of `LD_DW_IMM` instructions (spanning on 64 bits) are considered as two
-/// distinct instructions.
-/// let v = ebpf::to_insn_vec(prog);
-/// ebpf::Insn {
-/// imm: 0x55667788
-/// opc: 0,
-/// imm: 0x11223344
-pub fn to_insn_vec(prog: &[u8]) -> Vec<Insn> {
- if prog.len() % INSN_SIZE != 0 {
- "Error: eBPF program length must be a multiple of {:?} octets",
- INSN_SIZE
- while insn_ptr * INSN_SIZE < prog.len() {
- let insn = get_insn(prog, insn_ptr);
- res.push(insn);
@@ -1,488 +0,0 @@
-// Copyright 2015 Big Switch Networks, Inc
-// (Algorithms for uBPF helpers, originally in C)
-// (Translation to Rust, other helpers)
-//! This module implements some built-in helpers that can be called from within an eBPF program.
-//! These helpers may originate from several places:
-//! * Some of them mimic the helpers available in the Linux kernel.
-//! * Some of them were proposed as example helpers in uBPF and they were adapted here.
-//! * Other helpers may be specific to rbpf.
-//! The prototype for helpers is always the same: five `u64` as arguments, and a `u64` as a return
-//! value. Hence some helpers have unused arguments, or return a 0 value in all cases, in order to
-//! respect this convention.
-// Helpers associated to kernel helpers
-// See also linux/include/uapi/linux/bpf.h in Linux kernel sources.
-// bpf_ktime_getns()
-/// Index of helper `bpf_ktime_getns()`, equivalent to `bpf_time_getns()`, in Linux kernel, see
-/// <https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/bpf.h>.
-pub const BPF_KTIME_GETNS_IDX: u32 = 5;
-/// Get monotonic time (since boot time) in nanoseconds. All arguments are unused.
-/// use rbpf::helpers;
-/// let t = helpers::bpf_time_getns(0, 0, 0, 0, 0);
-/// let d = t / 10u64.pow(9) / 60 / 60 / 24;
-/// let h = (t / 10u64.pow(9) / 60 / 60) % 24;
-/// let m = (t / 10u64.pow(9) / 60 ) % 60;
-/// let s = (t / 10u64.pow(9)) % 60;
-/// let ns = t % 10u64.pow(9);
-/// println!("Uptime: {:#x} == {} days {}:{}:{}, {} ns", t, d, h, m, s, ns);
-#[allow(dead_code)]
-#[allow(unused_variables)]
-#[allow(deprecated)]
-pub fn bpf_time_getns(unused1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
- time::precise_time_ns()
-// bpf_trace_printk()
-/// Index of helper `bpf_trace_printk()`, equivalent to `bpf_trace_printf()`, in Linux kernel, see
-pub const BPF_TRACE_PRINTK_IDX: u32 = 6;
-/// Prints its **last three** arguments to standard output. The **first two** arguments are
-/// **unused**. Returns the number of bytes written.
-/// By ignoring the first two arguments, it creates a helper that will have a behavior similar to
-/// the one of the equivalent helper `bpf_trace_printk()` from Linux kernel.
-/// let res = helpers::bpf_trace_printf(0, 0, 1, 15, 32);
-/// assert_eq!(res as usize, "bpf_trace_printf: 0x1, 0xf, 0x20\n".len());
-/// This will print `bpf_trace_printf: 0x1, 0xf, 0x20`.
-/// The eBPF code needed to perform the call in this example would be nearly identical to the code
-/// obtained by compiling the following code from C to eBPF with clang:
-/// ```c
-/// #include <linux/bpf.h>
-/// #include "path/to/linux/samples/bpf/bpf_helpers.h"
-/// int main(struct __sk_buff *skb)
-/// {
-/// // Only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed.
-/// // See <https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/kernel/trace/bpf_trace.c>.
-/// char *fmt = "bpf_trace_printk %llx, %llx, %llx\n";
-/// return bpf_trace_printk(fmt, sizeof(fmt), 1, 15, 32);
-/// }
-/// This would equally print the three numbers in `/sys/kernel/debug/tracing` file each time the
-/// program is run.
-pub fn bpf_trace_printf(unused1: u64, unused2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
- println!("bpf_trace_printf: {arg3:#x}, {arg4:#x}, {arg5:#x}");
- let size_arg = |x| {
- if x == 0 {
- 1
- (x as f64).log(16.0).floor() as u64 + 1
- "bpf_trace_printf: 0x, 0x, 0x\n".len() as u64 + size_arg(arg3) + size_arg(arg4) + size_arg(arg5)
-// Helpers coming from uBPF <https://github.com/iovisor/ubpf/blob/master/vm/test.c>
-/// The idea is to assemble five bytes into a single `u64`. For compatibility with the helpers API,
-/// each argument must be a `u64`.
-/// let gathered = helpers::gather_bytes(0x11, 0x22, 0x33, 0x44, 0x55);
-/// assert_eq!(gathered, 0x1122334455);
-pub fn gather_bytes(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
- arg1.wrapping_shl(32)
- | arg2.wrapping_shl(24)
- | arg3.wrapping_shl(16)
- | arg4.wrapping_shl(8)
- | arg5
-/// Same as `void *memfrob(void *s, size_t n);` in `string.h` in C. See the GNU manual page (in
-/// section 3) for `memfrob`. The memory is directly modified, and the helper returns 0 in all
-/// cases. Arguments 3 to 5 are unused.
-/// let val: u64 = 0x112233;
-/// let val_ptr = &val as *const u64;
-/// helpers::memfrob(val_ptr as u64, 8, 0, 0, 0);
-/// assert_eq!(val, 0x2a2a2a2a2a3b0819);
-/// assert_eq!(val, 0x112233);
-pub fn memfrob(ptr: u64, len: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
- for i in 0..len {
- let mut p = (ptr + i) as *mut u8;
- *p ^= 0b101010;
- 0
-// TODO: Try again when asm!() is available in stable Rust.
-// #![feature(asm)]
-// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-// #[allow(unused_variables)]
-// pub fn memfrob (ptr: u64, len: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
-// unsafe {
-// asm!(
-// "mov $0xf0, %rax"
-// ::: "mov $0xf1, %rcx"
-// ::: "mov $0xf2, %rdx"
-// ::: "mov $0xf3, %rsi"
-// ::: "mov $0xf4, %rdi"
-// ::: "mov $0xf5, %r8"
-// ::: "mov $0xf6, %r9"
-// ::: "mov $0xf7, %r10"
-// ::: "mov $0xf8, %r11"
-// );
-// }
-// 0
-/// Compute and return the square root of argument 1, cast as a float. Arguments 2 to 5 are
-/// unused.
-/// let x = helpers::sqrti(9, 0, 0, 0, 0);
-/// assert_eq!(x, 3);
-#[cfg(feature = "std")] // sqrt is only available when using `std`
-pub fn sqrti(arg1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
- (arg1 as f64).sqrt() as u64
-/// C-like `strcmp`, return 0 if the strings are equal, and a non-null value otherwise.
-/// let foo = "This is a string.\0".as_ptr() as u64;
-/// let bar = "This is another sting.\0".as_ptr() as u64;
-/// assert!(helpers::strcmp(foo, foo, 0, 0, 0) == 0);
-/// assert!(helpers::strcmp(foo, bar, 0, 0, 0) != 0);
-pub fn strcmp(arg1: u64, arg2: u64, arg3: u64, unused4: u64, unused5: u64) -> u64 {
- // C-like strcmp, maybe shorter than converting the bytes to string and comparing?
- if arg1 == 0 || arg2 == 0 {
- return u64::MAX;
- let mut a = arg1;
- let mut b = arg2;
- let mut a_val = *(a as *const u8);
- let mut b_val = *(b as *const u8);
- while a_val == b_val && a_val != 0 && b_val != 0 {
- a += 1;
- b += 1;
- a_val = *(a as *const u8);
- b_val = *(b as *const u8);
- if a_val >= b_val {
- (a_val - b_val) as u64
- (b_val - a_val) as u64
-// Some additional helpers
-/// Returns a random u64 value comprised between `min` and `max` values (inclusive). Arguments 3 to
-/// 5 are unused.
-/// Relies on `rand()` function from libc, so `libc::srand()` should be called once before this
-/// helper is used.
-/// extern crate libc;
-/// extern crate rbpf;
-/// extern crate time;
-/// unsafe {
-/// libc::srand(time::precise_time_ns() as u32)
-/// let n = rbpf::helpers::rand(3, 6, 0, 0, 0);
-/// assert!(3 <= n && n <= 6);
-pub fn rand(min: u64, max: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 {
- let mut n = unsafe { (libc::rand() as u64).wrapping_shl(32) + libc::rand() as u64 };
- if min < max {
- n = n % (max + 1 - min) + min;
- n
-/// Prints the helper functions name and it's index.
-pub fn show_helper() {
- for (index, name) in BPF_FUNC_MAPPER.iter().enumerate() {
- println!("{}:{}", index, name);
-/// See https://github.com/torvalds/linux/blob/master/include/uapi/linux/bpf.h
-pub const BPF_FUNC_MAPPER: &[&str] = &[
- "unspec",
- "map_lookup_elem",
- "map_update_elem",
- "map_delete_elem",
- "probe_read",
- "ktime_get_ns",
- "trace_printk",
- "get_prandom_u32",
- "get_smp_processor_id",
- "skb_store_bytes",
- "l3_csum_replace",
- "l4_csum_replace",
- "tail_call",
- "clone_redirect",
- "get_current_pid_tgid",
- "get_current_uid_gid",
- "get_current_comm",
- "get_cgroup_classid",
- "skb_vlan_push",
- "skb_vlan_pop",
- "skb_get_tunnel_key",
- "skb_set_tunnel_key",
- "perf_event_read",
- "redirect",
- "get_route_realm",
- "perf_event_output",
- "skb_load_bytes",
- "get_stackid",
- "csum_diff",
- "skb_get_tunnel_opt",
- "skb_set_tunnel_opt",
- "skb_change_proto",
- "skb_change_type",
- "skb_under_cgroup",
- "get_hash_recalc",
- "get_current_task",
- "probe_write_user",
- "current_task_under_cgroup",
- "skb_change_tail",
- "skb_pull_data",
- "csum_update",
- "set_hash_invalid",
- "get_numa_node_id",
- "skb_change_head",
- "xdp_adjust_head",
- "probe_read_str",
- "get_socket_cookie",
- "get_socket_uid",
- "set_hash",
- "setsockopt",
- "skb_adjust_room",
- "redirect_map",
- "sk_redirect_map",
- "sock_map_update",
- "xdp_adjust_meta",
- "perf_event_read_value",
- "perf_prog_read_value",
- "getsockopt",
- "override_return",
- "sock_ops_cb_flags_set",
- "msg_redirect_map",
- "msg_apply_bytes",
- "msg_cork_bytes",
- "msg_pull_data",
- "bind",
- "xdp_adjust_tail",
- "skb_get_xfrm_state",
- "get_stack",
- "skb_load_bytes_relative",
- "fib_lookup",
- "sock_hash_update",
- "msg_redirect_hash",
- "sk_redirect_hash",
- "lwt_push_encap",
- "lwt_seg6_store_bytes",
- "lwt_seg6_adjust_srh",
- "lwt_seg6_action",
- "rc_repeat",
- "rc_keydown",
- "skb_cgroup_id",
- "get_current_cgroup_id",
- "get_local_storage",
- "sk_select_reuseport",
- "skb_ancestor_cgroup_id",
- "sk_lookup_tcp",
- "sk_lookup_udp",
- "sk_release",
- "map_push_elem",
- "map_pop_elem",
- "map_peek_elem",
- "msg_push_data",
- "msg_pop_data",
- "rc_pointer_rel",
- "spin_lock",
- "spin_unlock",
- "sk_fullsock",
- "tcp_sock",
- "skb_ecn_set_ce",
- "get_listener_sock",
- "skc_lookup_tcp",
- "tcp_check_syncookie",
- "sysctl_get_name",
- "sysctl_get_current_value",
- "sysctl_get_new_value",
- "sysctl_set_new_value",
- "strtol",
- "strtoul",
- "sk_storage_get",
- "sk_storage_delete",
- "send_signal",
- "tcp_gen_syncookie",
- "skb_output",
- "probe_read_user",
- "probe_read_kernel",
- "probe_read_user_str",
- "probe_read_kernel_str",
- "tcp_send_ack",
- "send_signal_thread",
- "jiffies64",
- "read_branch_records",
- "get_ns_current_pid_tgid",
- "xdp_output",
- "get_netns_cookie",
- "get_current_ancestor_cgroup_id",
- "sk_assign",
- "ktime_get_boot_ns",
- "seq_printf",
- "seq_write",
- "sk_cgroup_id",
- "sk_ancestor_cgroup_id",
- "ringbuf_output",
- "ringbuf_reserve",
- "ringbuf_submit",
- "ringbuf_discard",
- "ringbuf_query",
- "csum_level",
- "skc_to_tcp6_sock",
- "skc_to_tcp_sock",
- "skc_to_tcp_timewait_sock",
- "skc_to_tcp_request_sock",
- "skc_to_udp6_sock",
- "get_task_stack",
- "load_hdr_opt",
- "store_hdr_opt",
- "reserve_hdr_opt",
- "inode_storage_get",
- "inode_storage_delete",
- "d_path",
- "copy_from_user",
- "snprintf_btf",
- "seq_printf_btf",
- "skb_cgroup_classid",
- "redirect_neigh",
- "per_cpu_ptr",
- "this_cpu_ptr",
- "redirect_peer",
- "task_storage_get",
- "task_storage_delete",
- "get_current_task_btf",
- "bprm_opts_set",
- "ktime_get_coarse_ns",
- "ima_inode_hash",
- "sock_from_file",
- "check_mtu",
- "for_each_map_elem",
- "snprintf",
- "sys_bpf",
- "btf_find_by_name_kind",
- "sys_close",
- "timer_init",
- "timer_set_callback",
- "timer_start",
- "timer_cancel",
- "get_func_ip",
- "get_attach_cookie",
- "task_pt_regs",
- "get_branch_snapshot",
- "trace_vprintk",
- "skc_to_unix_sock",
- "kallsyms_lookup_name",
- "find_vma",
- "loop",
- "strncmp",
- "get_func_arg",
- "get_func_ret",
- "get_func_arg_cnt",
- "get_retval",
- "set_retval",
- "xdp_get_buff_len",
- "xdp_load_bytes",
- "xdp_store_bytes",
- "copy_from_user_task",
- "skb_set_tstamp",
- "ima_file_hash",
- "kptr_xchg",
- "map_lookup_percpu_elem",
- "skc_to_mptcp_sock",
- "dynptr_from_mem",
- "ringbuf_reserve_dynptr",
- "ringbuf_submit_dynptr",
- "ringbuf_discard_dynptr",
- "dynptr_read",
- "dynptr_write",
- "dynptr_data",
- "tcp_raw_gen_syncookie_ipv4",
- "tcp_raw_gen_syncookie_ipv6",
- "tcp_raw_check_syncookie_ipv4",
- "tcp_raw_check_syncookie_ipv6",
- "ktime_get_tai_ns",
- "user_ringbuf_drain",
- "cgrp_storage_get",
- "cgrp_storage_delete",
@@ -1,2199 +0,0 @@
-// Copyright 2017 Alex Dukhno <alex.dukhno@icloud.com>
-//! Module provides API to create eBPF programs by Rust programming language
-use crate::ebpf::*;
-/// Represents single eBPF instruction
-pub trait Instruction: Sized {
- /// returns instruction opt code
- fn opt_code_byte(&self) -> u8;
- /// returns destination register
- fn get_dst(&self) -> u8 {
- self.get_insn().dst
- /// returns source register
- fn get_src(&self) -> u8 {
- self.get_insn().src
- /// returns offset bytes
- fn get_off(&self) -> i16 {
- self.get_insn().off
- /// returns immediate value
- fn get_imm(&self) -> i32 {
- self.get_insn().imm
- /// sets destination register
- fn set_dst(mut self, dst: u8) -> Self {
- self.get_insn_mut().dst = dst;
- self
- /// sets source register
- fn set_src(mut self, src: u8) -> Self {
- self.get_insn_mut().src = src;
- /// sets offset bytes
- fn set_off(mut self, offset: i16) -> Self {
- self.get_insn_mut().off = offset;
- /// sets immediate value
- fn set_imm(mut self, imm: i32) -> Self {
- self.get_insn_mut().imm = imm;
- /// get `ebpf::Insn` struct
- fn get_insn(&self) -> &Insn;
- /// get mutable `ebpf::Insn` struct
- fn get_insn_mut(&mut self) -> &mut Insn;
-/// General trait for `Instruction`s and `BpfCode`.
-/// Provides functionality to transform `struct` into collection of bytes
-pub trait IntoBytes {
- /// type of targeted transformation
- type Bytes;
- /// consume `Self` with transformation into `Self::Bytes`
- fn into_bytes(self) -> Self::Bytes;
-/// General implementation of `IntoBytes` for `Instruction`
-impl<I: Instruction> IntoBytes for &'_ I {
- type Bytes = Vec<u8>;
- /// transform immutable reference of `Instruction` into `Vec<u8>` with size of 8
- /// [ 1 byte , 1 byte , 2 bytes, 4 bytes ]
- /// [ OP_CODE, SRC_REG | DST_REG, OFFSET , IMMEDIATE ]
- fn into_bytes(self) -> Self::Bytes {
- let buffer = vec![
- self.opt_code_byte(),
- self.get_src() << 4 | self.get_dst(),
- self.get_off() as u8,
- (self.get_off() >> 8) as u8,
- self.get_imm() as u8,
- (self.get_imm() >> 8) as u8,
- (self.get_imm() >> 16) as u8,
- (self.get_imm() >> 24) as u8,
- buffer
-/// BPF instruction stack in byte representation
-#[derive(Default)]
-pub struct BpfCode {
- instructions: Vec<u8>,
-impl BpfCode {
- /// creates new empty BPF instruction stack
- pub fn new() -> Self {
- BpfCode {
- instructions: vec![],
- /// create ADD instruction
- pub fn add(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::Add)
- /// create SUB instruction
- pub fn sub(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::Sub)
- /// create MUL instruction
- pub fn mul(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::Mul)
- /// create DIV instruction
- pub fn div(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::Div)
- /// create OR instruction
- pub fn bit_or(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::BitOr)
- /// create AND instruction
- pub fn bit_and(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::BitAnd)
- /// create LSHIFT instruction
- pub fn left_shift(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::LShift)
- /// create RSHIFT instruction
- pub fn right_shift(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::RShift)
- /// create NEGATE instruction
- pub fn negate(&mut self, arch: Arch) -> Move {
- self.mov_internal(Source::Imm, arch, OpBits::Negate)
- /// create MOD instruction
- pub fn modulo(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::Mod)
- /// create XOR instruction
- pub fn bit_xor(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::BitXor)
- /// create MOV instruction
- pub fn mov(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::Mov)
- /// create SIGNED RSHIFT instruction
- pub fn signed_right_shift(&mut self, source: Source, arch: Arch) -> Move {
- self.mov_internal(source, arch, OpBits::SignRShift)
- #[inline]
- fn mov_internal(&mut self, source: Source, arch_bits: Arch, op_bits: OpBits) -> Move {
- Move {
- bpf_code: self,
- src_bit: source,
- op_bits,
- arch_bits,
- insn: Insn {
- opc: 0x00,
- dst: 0x00,
- src: 0x00,
- off: 0x00_00,
- imm: 0x00_00_00_00,
- /// create byte swap instruction
- pub fn swap_bytes(&mut self, endian: Endian) -> SwapBytes {
- SwapBytes {
- endian,
- /// create LOAD instruction, IMMEDIATE is the source
- pub fn load(&mut self, mem_size: MemSize) -> Load {
- self.load_internal(mem_size, Addressing::Imm, BPF_LD)
- /// create ABSOLUTE LOAD instruction
- pub fn load_abs(&mut self, mem_size: MemSize) -> Load {
- self.load_internal(mem_size, Addressing::Abs, BPF_LD)
- /// create INDIRECT LOAD instruction
- pub fn load_ind(&mut self, mem_size: MemSize) -> Load {
- self.load_internal(mem_size, Addressing::Ind, BPF_LD)
- /// create LOAD instruction, MEMORY is the source
- pub fn load_x(&mut self, mem_size: MemSize) -> Load {
- self.load_internal(mem_size, Addressing::Mem, BPF_LDX)
- fn load_internal(&mut self, mem_size: MemSize, addressing: Addressing, source: u8) -> Load {
- Load {
- addressing,
- mem_size,
- source,
- /// creates STORE instruction, IMMEDIATE is the source
- pub fn store(&mut self, mem_size: MemSize) -> Store {
- self.store_internal(mem_size, BPF_IMM)
- /// creates STORE instruction, MEMORY is the source
- pub fn store_x(&mut self, mem_size: MemSize) -> Store {
- self.store_internal(mem_size, BPF_MEM | BPF_STX)
- fn store_internal(&mut self, mem_size: MemSize, source: u8) -> Store {
- Store {
- /// create unconditional JMP instruction
- pub fn jump_unconditional(&mut self) -> Jump {
- self.jump_conditional(Cond::Abs, Source::Imm)
- /// create conditional JMP instruction
- pub fn jump_conditional(&mut self, cond: Cond, src_bit: Source) -> Jump {
- Jump {
- cond,
- src_bit,
- /// create CALL instruction
- pub fn call(&mut self) -> FunctionCall {
- FunctionCall {
- /// create EXIT instruction
- pub fn exit(&mut self) -> Exit {
- Exit {
-/// Transform `BpfCode` into assemble representation
-impl<'a> IntoBytes for &'a BpfCode {
- type Bytes = &'a [u8];
- /// returns `BpfCode` instruction stack as `&[u8]`
- self.instructions.as_slice()
-/// struct to represent `MOV ALU` instructions
-pub struct Move<'i> {
- bpf_code: &'i mut BpfCode,
- src_bit: Source,
- op_bits: OpBits,
- arch_bits: Arch,
- insn: Insn,
-impl<'i> Move<'i> {
- /// push MOV instruction into BpfCode instruction stack
- pub fn push(self) -> &'i mut BpfCode {
- let mut asm = self.into_bytes();
- self.bpf_code.instructions.append(&mut asm);
- self.bpf_code
-impl Instruction for Move<'_> {
- fn opt_code_byte(&self) -> u8 {
- let op_bits = self.op_bits as u8;
- let src_bit = self.src_bit as u8;
- let arch_bits = self.arch_bits as u8;
- op_bits | src_bit | arch_bits
- fn get_insn_mut(&mut self) -> &mut Insn {
- &mut self.insn
- fn get_insn(&self) -> &Insn {
- &self.insn
-#[derive(Copy, Clone, PartialEq, Eq)]
-/// The source of ALU and JMP instructions
-pub enum Source {
- /// immediate field will be used as a source
- Imm = BPF_IMM as isize,
- /// src register will be used as a source
- Reg = BPF_X as isize,
-#[derive(Copy, Clone)]
-enum OpBits {
- Add = BPF_ADD as isize,
- Sub = BPF_SUB as isize,
- Mul = BPF_MUL as isize,
- Div = BPF_DIV as isize,
- BitOr = BPF_OR as isize,
- BitAnd = BPF_AND as isize,
- LShift = BPF_LSH as isize,
- RShift = BPF_RSH as isize,
- Negate = BPF_NEG as isize,
- Mod = BPF_MOD as isize,
- BitXor = BPF_XOR as isize,
- Mov = BPF_MOV as isize,
- SignRShift = BPF_ARSH as isize,
-/// Architecture of instructions
-pub enum Arch {
- /// 64-bit instructions
- X64 = BPF_ALU64 as isize,
- /// 32-bit instructions
- X32 = BPF_ALU as isize,
-/// struct representation of byte swap operation
-pub struct SwapBytes<'i> {
- endian: Endian,
-impl<'i> SwapBytes<'i> {
- /// push bytes swap instruction into BpfCode instruction stack
-impl Instruction for SwapBytes<'_> {
- self.endian as u8
-/// Bytes endian
-pub enum Endian {
- /// Little endian
- Little = LE as isize,
- /// Big endian
- Big = BE as isize,
-/// struct representation of LOAD instructions
-pub struct Load<'i> {
- addressing: Addressing,
- mem_size: MemSize,
- source: u8,
-impl<'i> Load<'i> {
- /// push LOAD instruction into BpfCode instruction stack
-impl Instruction for Load<'_> {
- let size = self.mem_size as u8;
- let addressing = self.addressing as u8;
- addressing | size | self.source
-/// struct representation of STORE instructions
-pub struct Store<'i> {
-impl<'i> Store<'i> {
- /// push STORE instruction into BpfCode instruction stack
-impl Instruction for Store<'_> {
- BPF_MEM | BPF_ST | size | self.source
-/// Memory size for LOAD and STORE instructions
-pub enum MemSize {
- /// 8-bit size
- Byte = BPF_B as isize,
- /// 16-bit size
- HalfWord = BPF_H as isize,
- /// 32-bit size
- Word = BPF_W as isize,
- /// 64-bit size
- DoubleWord = BPF_DW as isize,
-enum Addressing {
- Abs = BPF_ABS as isize,
- Ind = BPF_IND as isize,
- Mem = BPF_MEM as isize,
-/// struct representation of JMP instructions
-pub struct Jump<'i> {
- cond: Cond,
-impl<'i> Jump<'i> {
- /// push JMP instruction into BpfCode instruction stack
-impl Instruction for Jump<'_> {
- let cmp: u8 = self.cond as u8;
- cmp | src_bit | BPF_JMP
-/// Conditions for JMP instructions
-pub enum Cond {
- /// Absolute or unconditional
- Abs = BPF_JA as isize,
- /// Jump if `==`
- Equals = BPF_JEQ as isize,
- /// Jump if `>`
- Greater = BPF_JGT as isize,
- /// Jump if `>=`
- GreaterEquals = BPF_JGE as isize,
- /// Jump if `<`
- Lower = BPF_JLT as isize,
- /// Jump if `<=`
- LowerEquals = BPF_JLE as isize,
- /// Jump if `src` & `dst`
- BitAnd = BPF_JSET as isize,
- /// Jump if `!=`
- NotEquals = BPF_JNE as isize,
- /// Jump if `>` (signed)
- GreaterSigned = BPF_JSGT as isize,
- /// Jump if `>=` (signed)
- GreaterEqualsSigned = BPF_JSGE as isize,
- /// Jump if `<` (signed)
- LowerSigned = BPF_JSLT as isize,
- /// Jump if `<=` (signed)
- LowerEqualsSigned = BPF_JSLE as isize,
-/// struct representation of CALL instruction
-pub struct FunctionCall<'i> {
-impl<'i> FunctionCall<'i> {
- /// push CALL instruction into BpfCode instruction stack
-impl Instruction for FunctionCall<'_> {
- BPF_CALL | BPF_JMP
-/// struct representation of EXIT instruction
-pub struct Exit<'i> {
-impl<'i> Exit<'i> {
- /// push EXIT instruction into BpfCode instruction stack
-impl Instruction for Exit<'_> {
- BPF_EXIT | BPF_JMP
- #[cfg(test)]
- mod special {
- use super::super::*;
- fn call_immediate() {
- let mut program = BpfCode::new();
- program.call().set_imm(0x11_22_33_44).push();
- program.into_bytes(),
- &[0x85, 0x00, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11]
- fn exit_operation() {
- program.exit().push();
- &[0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- mod jump_instructions {
- mod register {
- use super::super::super::*;
- fn jump_on_dst_equals_src() {
- program
- .jump_conditional(Cond::Equals, Source::Reg)
- .set_dst(0x01)
- .set_src(0x02)
- .push();
- &[0x1d, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_greater_than_src() {
- .jump_conditional(Cond::Greater, Source::Reg)
- .set_dst(0x03)
- &[0x2d, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_greater_or_equals_to_src() {
- .jump_conditional(Cond::GreaterEquals, Source::Reg)
- .set_dst(0x04)
- .set_src(0x01)
- &[0x3d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_lower_than_src() {
- .jump_conditional(Cond::Lower, Source::Reg)
- &[0xad, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_lower_or_equals_to_src() {
- .jump_conditional(Cond::LowerEquals, Source::Reg)
- &[0xbd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_bit_and_with_src_not_equal_zero() {
- .jump_conditional(Cond::BitAnd, Source::Reg)
- .set_dst(0x05)
- &[0x4d, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_not_equals_src() {
- .jump_conditional(Cond::NotEquals, Source::Reg)
- .set_src(0x05)
- &[0x5d, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_greater_than_src_signed() {
- .jump_conditional(Cond::GreaterSigned, Source::Reg)
- &[0x6d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_greater_or_equals_src_signed() {
- .jump_conditional(Cond::GreaterEqualsSigned, Source::Reg)
- .set_src(0x03)
- &[0x7d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_lower_than_src_signed() {
- .jump_conditional(Cond::LowerSigned, Source::Reg)
- &[0xcd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_lower_or_equals_src_signed() {
- .jump_conditional(Cond::LowerEqualsSigned, Source::Reg)
- &[0xdd, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- mod immediate {
- fn jump_to_label() {
- program.jump_unconditional().set_off(0x00_11).push();
- &[0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_equals_const() {
- .jump_conditional(Cond::Equals, Source::Imm)
- .set_imm(0x00_11_22_33)
- &[0x15, 0x01, 0x00, 0x00, 0x33, 0x22, 0x11, 0x00]
- fn jump_on_dst_greater_than_const() {
- .jump_conditional(Cond::Greater, Source::Imm)
- .set_dst(0x02)
- .set_imm(0x00_11_00_11)
- &[0x25, 0x02, 0x00, 0x00, 0x11, 0x00, 0x11, 0x00]
- fn jump_on_dst_greater_or_equals_to_const() {
- .jump_conditional(Cond::GreaterEquals, Source::Imm)
- .set_imm(0x00_22_11_00)
- &[0x35, 0x04, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00]
- fn jump_on_dst_lower_than_const() {
- .jump_conditional(Cond::Lower, Source::Imm)
- &[0xa5, 0x02, 0x00, 0x00, 0x11, 0x00, 0x11, 0x00]
- fn jump_on_dst_lower_or_equals_to_const() {
- .jump_conditional(Cond::LowerEquals, Source::Imm)
- &[0xb5, 0x04, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00]
- fn jump_on_dst_bit_and_with_const_not_equal_zero() {
- .jump_conditional(Cond::BitAnd, Source::Imm)
- &[0x45, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_not_equals_const() {
- .jump_conditional(Cond::NotEquals, Source::Imm)
- &[0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_greater_than_const_signed() {
- .jump_conditional(Cond::GreaterSigned, Source::Imm)
- &[0x65, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .jump_conditional(Cond::GreaterEqualsSigned, Source::Imm)
- &[0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn jump_on_dst_lower_than_const_signed() {
- .jump_conditional(Cond::LowerSigned, Source::Imm)
- &[0xc5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .jump_conditional(Cond::LowerEqualsSigned, Source::Imm)
- &[0xd5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- mod store_instructions {
- fn store_word_from_dst_into_immediate_address() {
- .store(MemSize::Word)
- .set_off(0x00_11)
- .set_imm(0x11_22_33_44)
- &[0x62, 0x01, 0x11, 0x00, 0x44, 0x33, 0x22, 0x11]
- fn store_half_word_from_dst_into_immediate_address() {
- .store(MemSize::HalfWord)
- .set_off(0x11_22)
- &[0x6a, 0x02, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00]
- fn store_byte_from_dst_into_immediate_address() {
- program.store(MemSize::Byte).push();
- &[0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn store_double_word_from_dst_into_immediate_address() {
- program.store(MemSize::DoubleWord).push();
- &[0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn store_word_from_dst_into_src_address() {
- .store_x(MemSize::Word)
- &[0x63, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn store_half_word_from_dst_into_src_address() {
- program.store_x(MemSize::HalfWord).push();
- &[0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn store_byte_from_dst_into_src_address() {
- program.store_x(MemSize::Byte).push();
- &[0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn store_double_word_from_dst_into_src_address() {
- program.store_x(MemSize::DoubleWord).push();
- &[0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- mod load_instructions {
- fn load_word_from_set_src_with_offset() {
- .load_x(MemSize::Word)
- .set_off(0x00_02)
- &[0x61, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn load_half_word_from_set_src_with_offset() {
- .load_x(MemSize::HalfWord)
- &[0x69, 0x12, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00]
- fn load_byte_from_set_src_with_offset() {
- .load_x(MemSize::Byte)
- .set_src(0x04)
- &[0x71, 0x41, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn load_double_word_from_set_src_with_offset() {
- .load_x(MemSize::DoubleWord)
- .set_off(0x44_55)
- &[0x79, 0x54, 0x55, 0x44, 0x00, 0x00, 0x00, 0x00]
- fn load_double_word() {
- .load(MemSize::DoubleWord)
- .set_imm(0x00_01_02_03)
- &[0x18, 0x01, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00]
- fn load_abs_word() {
- program.load_abs(MemSize::Word).push();
- &[0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn load_abs_half_word() {
- program.load_abs(MemSize::HalfWord).set_dst(0x05).push();
- &[0x28, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn load_abs_byte() {
- program.load_abs(MemSize::Byte).set_dst(0x01).push();
- &[0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn load_abs_double_word() {
- .load_abs(MemSize::DoubleWord)
- .set_imm(0x01_02_03_04)
- &[0x38, 0x01, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01]
- fn load_indirect_word() {
- program.load_ind(MemSize::Word).push();
- &[0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn load_indirect_half_word() {
- program.load_ind(MemSize::HalfWord).push();
- &[0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn load_indirect_byte() {
- program.load_ind(MemSize::Byte).push();
- &[0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn load_indirect_double_word() {
- program.load_ind(MemSize::DoubleWord).push();
- &[0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- mod byte_swap_instructions {
- fn convert_host_to_little_endian_16bits() {
- .swap_bytes(Endian::Little)
- .set_imm(0x00_00_00_10)
- &[0xd4, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00]
- fn convert_host_to_little_endian_32bits() {
- .set_imm(0x00_00_00_20)
- &[0xd4, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00]
- fn convert_host_to_little_endian_64bit() {
- .set_imm(0x00_00_00_40)
- &[0xd4, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00]
- fn convert_host_to_big_endian_16bits() {
- .swap_bytes(Endian::Big)
- &[0xdc, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00]
- fn convert_host_to_big_endian_32bits() {
- &[0xdc, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00]
- fn convert_host_to_big_endian_64bit() {
- &[0xdc, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00]
- mod moves_instructions {
- mod arch_x64 {
- use super::super::super::super::*;
- fn move_and_add_const_to_register() {
- .add(Source::Imm, Arch::X64)
- &[0x07, 0x02, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01]
- fn move_sub_const_to_register() {
- .sub(Source::Imm, Arch::X64)
- &[0x17, 0x04, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00]
- fn move_mul_const_to_register() {
- .mul(Source::Imm, Arch::X64)
- .set_imm(0x04_03_02_01)
- &[0x27, 0x05, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04]
- fn move_div_constant_to_register() {
- .div(Source::Imm, Arch::X64)
- .set_imm(0x00_ff_00_ff)
- &[0x37, 0x02, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00]
- fn move_bit_or_const_to_register() {
- .bit_or(Source::Imm, Arch::X64)
- .set_imm(0x00_11_00_22)
- &[0x47, 0x02, 0x00, 0x00, 0x22, 0x00, 0x11, 0x00]
- fn move_bit_and_const_to_register() {
- .bit_and(Source::Imm, Arch::X64)
- &[0x57, 0x02, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11]
- fn move_left_shift_const_to_register() {
- .left_shift(Source::Imm, Arch::X64)
- &[0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_logical_right_shift_const_to_register() {
- .right_shift(Source::Imm, Arch::X64)
- &[0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_negate_register() {
- program.negate(Arch::X64).set_dst(0x02).push();
- &[0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_mod_const_to_register() {
- program.modulo(Source::Imm, Arch::X64).set_dst(0x02).push();
- &[0x97, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_bit_xor_const_to_register() {
- program.bit_xor(Source::Imm, Arch::X64).set_dst(0x03).push();
- &[0xa7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_const_to_register() {
- .mov(Source::Imm, Arch::X64)
- .set_imm(0x00_00_00_FF)
- &[0xb7, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00]
- fn move_signed_right_shift_const_to_register() {
- .signed_right_shift(Source::Imm, Arch::X64)
- &[0xc7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_and_add_from_register() {
- .add(Source::Reg, Arch::X64)
- &[0x0f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_sub_from_register_to_register() {
- .sub(Source::Reg, Arch::X64)
- &[0x1f, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_mul_from_register_to_register() {
- .mul(Source::Reg, Arch::X64)
- &[0x2f, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_div_from_register_to_register() {
- .div(Source::Reg, Arch::X64)
- .set_src(0x00)
- &[0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_bit_or_from_register_to_register() {
- .bit_or(Source::Reg, Arch::X64)
- &[0x4f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_bit_and_from_register_to_register() {
- .bit_and(Source::Reg, Arch::X64)
- &[0x5f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_left_shift_from_register_to_register() {
- .left_shift(Source::Reg, Arch::X64)
- &[0x6f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_logical_right_shift_from_register_to_register() {
- .right_shift(Source::Reg, Arch::X64)
- &[0x7f, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_mod_from_register_to_register() {
- .modulo(Source::Reg, Arch::X64)
- &[0x9f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_bit_xor_from_register_to_register() {
- .bit_xor(Source::Reg, Arch::X64)
- &[0xaf, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_from_register_to_another_register() {
- program.mov(Source::Reg, Arch::X64).set_src(0x01).push();
- &[0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- fn move_signed_right_shift_from_register_to_register() {
- .signed_right_shift(Source::Reg, Arch::X64)
- &[0xcf, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- mod arch_x32 {
- .add(Source::Imm, Arch::X32)
- &[0x04, 0x02, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01]
- .sub(Source::Imm, Arch::X32)
- &[0x14, 0x04, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00]
- .mul(Source::Imm, Arch::X32)
- &[0x24, 0x05, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04]
- .div(Source::Imm, Arch::X32)
- &[0x34, 0x02, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00]
- .bit_or(Source::Imm, Arch::X32)
- &[0x44, 0x02, 0x00, 0x00, 0x22, 0x00, 0x11, 0x00]
- .bit_and(Source::Imm, Arch::X32)
- &[0x54, 0x02, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11]
- .left_shift(Source::Imm, Arch::X32)
- &[0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .right_shift(Source::Imm, Arch::X32)
- &[0x74, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- program.negate(Arch::X32).set_dst(0x02).push();
- &[0x84, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- program.modulo(Source::Imm, Arch::X32).set_dst(0x02).push();
- &[0x94, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- program.bit_xor(Source::Imm, Arch::X32).set_dst(0x03).push();
- &[0xa4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .mov(Source::Imm, Arch::X32)
- &[0xb4, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00]
- .signed_right_shift(Source::Imm, Arch::X32)
- &[0xc4, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .add(Source::Reg, Arch::X32)
- &[0x0c, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .sub(Source::Reg, Arch::X32)
- &[0x1c, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .mul(Source::Reg, Arch::X32)
- &[0x2c, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .div(Source::Reg, Arch::X32)
- &[0x3c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .bit_or(Source::Reg, Arch::X32)
- &[0x4c, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .bit_and(Source::Reg, Arch::X32)
- &[0x5c, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .left_shift(Source::Reg, Arch::X32)
- &[0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .right_shift(Source::Reg, Arch::X32)
- &[0x7c, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .modulo(Source::Reg, Arch::X32)
- &[0x9c, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .bit_xor(Source::Reg, Arch::X32)
- &[0xac, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .mov(Source::Reg, Arch::X32)
- .set_dst(0x00)
- &[0xbc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- .signed_right_shift(Source::Reg, Arch::X32)
- &[0xcc, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- mod programs {
- fn example_from_assembler() {
- .set_dst(1)
- .set_imm(0x605)
- .push()
- .set_dst(2)
- .set_imm(0x32)
- .mov(Source::Reg, Arch::X64)
- .set_src(0)
- .set_dst(0)
- .set_imm(0x10)
- .negate(Arch::X64)
- .exit()
- let bytecode = program.into_bytes();
- let ref_prog = &[
- 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00,
- 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- // cargo says: "`[{integer}; 48]` cannot be formatted using `{:?}`
- // because it doesn't implement `std::fmt::Debug`"
- // So let's check in two steps.
- assert_eq!(bytecode[..32], ref_prog[..32]);
- assert_eq!(bytecode[33..], ref_prog[33..]);
@@ -1,708 +0,0 @@
-// Derived from uBPF <https://github.com/iovisor/ubpf>
-// (uBPF: VM architecture, parts of the interpreter, originally in C)
-// (Translation to Rust, MetaBuff/multiple classes addition, hashmaps for helpers)
- helpers::BPF_FUNC_MAPPER,
- stack::StackFrame,
- *,
-#[cfg(not(feature = "user"))]
-#[allow(unused)]
-fn check_mem(
- addr: u64,
- len: usize,
- access_type: &str,
- insn_ptr: usize,
- mbuff: &[u8],
- mem: &[u8],
- stack: &[u8],
-) -> Result<(), Error> {
- log::trace!(
- "check_mem: addr {:#x}, len {}, access_type {}, insn_ptr {}",
- addr,
- len,
- access_type,
- insn_ptr
- "check_mem: mbuff: {:#x}/{:#x}, mem: {:#x}/{:#x}, stack: {:#x}/{:#x}",
- mbuff.as_ptr() as u64,
- mbuff.len(),
- mem.as_ptr() as u64,
- mem.len(),
- stack.as_ptr() as u64,
- stack.len()
-#[cfg(feature = "user")]
- if let Some(addr_end) = addr.checked_add(len as u64) {
- if mbuff.as_ptr() as u64 <= addr && addr_end <= mbuff.as_ptr() as u64 + mbuff.len() as u64 {
- return Ok(());
- if mem.as_ptr() as u64 <= addr && addr_end <= mem.as_ptr() as u64 + mem.len() as u64 {
- if stack.as_ptr() as u64 <= addr && addr_end <= stack.as_ptr() as u64 + stack.len() as u64 {
- Err(Error::new(ErrorKind::Other, format!(
- "Error: out of bounds memory {} (insn #{:?}), addr {:#x}, size {:?}\nmbuff: {:#x}/{:#x}, mem: {:#x}/{:#x}, stack: {:#x}/{:#x}",
- access_type, insn_ptr, addr, len,
- mbuff.as_ptr() as u64, mbuff.len(),
- mem.as_ptr() as u64, mem.len(),
- stack.as_ptr() as u64, stack.len()
- )))
-fn do_jump(insn_ptr: &mut usize, insn: &Insn) {
- *insn_ptr = (*insn_ptr as i16 + insn.off) as usize;
-#[allow(cyclomatic_complexity)]
-pub fn execute_program(
- prog_: Option<&[u8]>,
- helpers: &HashMap<u32, ebpf::Helper>,
-) -> Result<u64, Error> {
- const U32MAX: u64 = u32::MAX as u64;
- const SHIFT_MASK_64: u64 = 0x3f;
- let prog = match prog_ {
- Some(prog) => prog,
- None => Err(Error::new(
- "Error: No program set, call prog_set() to load one",
- ))?,
- let mut stacks = Vec::new();
- let stack = StackFrame::new();
- // R1 points to beginning of memory area, R10 to stack
- let mut reg: [u64; 11] = [
- 0,
- stack.as_ptr() as u64 + stack.len() as u64,
- stacks.push(stack);
- if !mbuff.is_empty() {
- reg[1] = mbuff.as_ptr() as u64;
- } else if !mem.is_empty() {
- reg[1] = mem.as_ptr() as u64;
- let check_mem_load =
- |stack: &[u8], addr: u64, len: usize, insn_ptr: usize| -> Result<(), Error> {
- check_mem(addr, len, "load", insn_ptr, mbuff, mem, stack)
- let check_mem_store =
- check_mem(addr, len, "store", insn_ptr, mbuff, mem, stack)
- // Loop on instructions
- let _dst = insn.dst as usize;
- let _src = insn.src as usize;
- reg[0] = unsafe {
- let x = (mem.as_ptr() as u64 + (insn.imm as u32) as u64) as *const u8;
- check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?;
- x.read_unaligned() as u64
- let x = (mem.as_ptr() as u64 + (insn.imm as u32) as u64) as *const u16;
- let x = (mem.as_ptr() as u64 + (insn.imm as u32) as u64) as *const u32;
- log::info!("executing LD_ABS_DW, set reg[{}] to {:#x}", _dst, insn.imm);
- let x = (mem.as_ptr() as u64 + (insn.imm as u32) as u64) as *const u64;
- x.read_unaligned()
- let x =
- (mem.as_ptr() as u64 + reg[_src] + (insn.imm as u32) as u64) as *const u8;
- (mem.as_ptr() as u64 + reg[_src] + (insn.imm as u32) as u64) as *const u16;
- (mem.as_ptr() as u64 + reg[_src] + (insn.imm as u32) as u64) as *const u32;
- (mem.as_ptr() as u64 + reg[_src] + (insn.imm as u32) as u64) as *const u64;
- // log::warn!(
- // "executing LD_DW_IMM, set reg[{}] to {:#x}",
- // _dst,
- // ((insn.imm as u32) as u64) + ((next_insn.imm as u64) << 32)
- // );
- reg[_dst] = ((insn.imm as u32) as u64) + ((next_insn.imm as u64) << 32);
- reg[_dst] = unsafe {
- #[allow(clippy::cast_ptr_alignment)]
- let x = (reg[_src] as *const u8).offset(insn.off as isize);
- check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 1, insn_ptr)?;
- let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u16;
- check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 2, insn_ptr)?;
- let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u32;
- check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 4, insn_ptr)?;
- // "executing LD_W_REG, the ptr is REG:{} -> [{:#x}] + {:#x}",
- // _src,
- // reg[_src],
- // insn.off
- let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u64;
- ebpf::ST_B_IMM => unsafe {
- let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u8;
- check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 1, insn_ptr)?;
- x.write_unaligned(insn.imm as u8);
- ebpf::ST_H_IMM => unsafe {
- let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u16;
- check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 2, insn_ptr)?;
- x.write_unaligned(insn.imm as u16);
- ebpf::ST_W_IMM => unsafe {
- let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u32;
- check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 4, insn_ptr)?;
- x.write_unaligned(insn.imm as u32);
- ebpf::ST_DW_IMM => unsafe {
- let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u64;
- check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?;
- x.write_unaligned(insn.imm as u64);
- ebpf::ST_B_REG => unsafe {
- x.write_unaligned(reg[_src] as u8);
- ebpf::ST_H_REG => unsafe {
- x.write_unaligned(reg[_src] as u16);
- ebpf::ST_W_REG => unsafe {
- x.write_unaligned(reg[_src] as u32);
- ebpf::ST_DW_REG => unsafe {
- x.write_unaligned(reg[_src]);
- ebpf::ADD32_IMM => reg[_dst] = (reg[_dst] as i32).wrapping_add(insn.imm) as u64, //((reg[_dst] & U32MAX) + insn.imm as u64) & U32MAX,
- ebpf::ADD32_REG => reg[_dst] = (reg[_dst] as i32).wrapping_add(reg[_src] as i32) as u64, //((reg[_dst] & U32MAX) + (reg[_src] & U32MAX)) & U32MAX,
- ebpf::SUB32_IMM => reg[_dst] = (reg[_dst] as i32).wrapping_sub(insn.imm) as u64,
- ebpf::SUB32_REG => reg[_dst] = (reg[_dst] as i32).wrapping_sub(reg[_src] as i32) as u64,
- ebpf::MUL32_IMM => reg[_dst] = (reg[_dst] as i32).wrapping_mul(insn.imm) as u64,
- ebpf::MUL32_REG => reg[_dst] = (reg[_dst] as i32).wrapping_mul(reg[_src] as i32) as u64,
- ebpf::DIV32_IMM if insn.imm as u32 == 0 => reg[_dst] = 0,
- ebpf::DIV32_IMM => reg[_dst] = (reg[_dst] as u32 / insn.imm as u32) as u64,
- ebpf::DIV32_REG if reg[_src] as u32 == 0 => reg[_dst] = 0,
- ebpf::DIV32_REG => reg[_dst] = (reg[_dst] as u32 / reg[_src] as u32) as u64,
- ebpf::OR32_IMM => reg[_dst] = (reg[_dst] as u32 | insn.imm as u32) as u64,
- ebpf::OR32_REG => reg[_dst] = (reg[_dst] as u32 | reg[_src] as u32) as u64,
- ebpf::AND32_IMM => reg[_dst] = (reg[_dst] as u32 & insn.imm as u32) as u64,
- ebpf::AND32_REG => reg[_dst] = (reg[_dst] as u32 & reg[_src] as u32) as u64,
- // As for the 64-bit version, we should mask the number of bits to shift with
- // 0x1f, but .wrappping_shr() already takes care of it for us.
- ebpf::LSH32_IMM => reg[_dst] = (reg[_dst] as u32).wrapping_shl(insn.imm as u32) as u64,
- ebpf::LSH32_REG => reg[_dst] = (reg[_dst] as u32).wrapping_shl(reg[_src] as u32) as u64,
- ebpf::RSH32_IMM => reg[_dst] = (reg[_dst] as u32).wrapping_shr(insn.imm as u32) as u64,
- ebpf::RSH32_REG => reg[_dst] = (reg[_dst] as u32).wrapping_shr(reg[_src] as u32) as u64,
- reg[_dst] = (reg[_dst] as i32).wrapping_neg() as u64;
- reg[_dst] &= U32MAX;
- ebpf::MOD32_IMM if insn.imm as u32 == 0 => (),
- ebpf::MOD32_IMM => reg[_dst] = (reg[_dst] as u32 % insn.imm as u32) as u64,
- ebpf::MOD32_REG if reg[_src] as u32 == 0 => (),
- ebpf::MOD32_REG => reg[_dst] = (reg[_dst] as u32 % reg[_src] as u32) as u64,
- ebpf::XOR32_IMM => reg[_dst] = (reg[_dst] as u32 ^ insn.imm as u32) as u64,
- ebpf::XOR32_REG => reg[_dst] = (reg[_dst] as u32 ^ reg[_src] as u32) as u64,
- ebpf::MOV32_IMM => reg[_dst] = insn.imm as u32 as u64,
- ebpf::MOV32_REG => reg[_dst] = (reg[_src] as u32) as u64,
- reg[_dst] = (reg[_dst] as i32).wrapping_shr(insn.imm as u32) as u64;
- reg[_dst] = (reg[_dst] as i32).wrapping_shr(reg[_src] as u32) as u64;
- reg[_dst] = match insn.imm {
- 16 => (reg[_dst] as u16).to_le() as u64,
- 32 => (reg[_dst] as u32).to_le() as u64,
- 64 => reg[_dst].to_le(),
- 16 => (reg[_dst] as u16).to_be() as u64,
- 32 => (reg[_dst] as u32).to_be() as u64,
- 64 => reg[_dst].to_be(),
- ebpf::ADD64_IMM => reg[_dst] = reg[_dst].wrapping_add(insn.imm as u64),
- ebpf::ADD64_REG => reg[_dst] = reg[_dst].wrapping_add(reg[_src]),
- ebpf::SUB64_IMM => reg[_dst] = reg[_dst].wrapping_sub(insn.imm as u64),
- ebpf::SUB64_REG => reg[_dst] = reg[_dst].wrapping_sub(reg[_src]),
- ebpf::MUL64_IMM => reg[_dst] = reg[_dst].wrapping_mul(insn.imm as u64),
- ebpf::MUL64_REG => reg[_dst] = reg[_dst].wrapping_mul(reg[_src]),
- ebpf::DIV64_IMM if insn.imm == 0 => reg[_dst] = 0,
- ebpf::DIV64_IMM => reg[_dst] /= insn.imm as u64,
- ebpf::DIV64_REG if reg[_src] == 0 => reg[_dst] = 0,
- ebpf::DIV64_REG => reg[_dst] /= reg[_src],
- ebpf::OR64_IMM => reg[_dst] |= insn.imm as u64,
- ebpf::OR64_REG => reg[_dst] |= reg[_src],
- ebpf::AND64_IMM => reg[_dst] &= insn.imm as u64,
- ebpf::AND64_REG => reg[_dst] &= reg[_src],
- ebpf::LSH64_IMM => reg[_dst] <<= insn.imm as u64 & SHIFT_MASK_64,
- ebpf::LSH64_REG => reg[_dst] <<= reg[_src] & SHIFT_MASK_64,
- ebpf::RSH64_IMM => reg[_dst] >>= insn.imm as u64 & SHIFT_MASK_64,
- ebpf::RSH64_REG => reg[_dst] >>= reg[_src] & SHIFT_MASK_64,
- ebpf::NEG64 => reg[_dst] = -(reg[_dst] as i64) as u64,
- ebpf::MOD64_IMM if insn.imm == 0 => (),
- ebpf::MOD64_IMM => reg[_dst] %= insn.imm as u64,
- ebpf::MOD64_REG if reg[_src] == 0 => (),
- ebpf::MOD64_REG => reg[_dst] %= reg[_src],
- ebpf::XOR64_IMM => reg[_dst] ^= insn.imm as u64,
- ebpf::XOR64_REG => reg[_dst] ^= reg[_src],
- ebpf::MOV64_IMM => reg[_dst] = insn.imm as u64,
- ebpf::MOV64_REG => reg[_dst] = reg[_src],
- reg[_dst] = (reg[_dst] as i64 >> (insn.imm as u64 & SHIFT_MASK_64)) as u64
- reg[_dst] = (reg[_dst] as i64 >> (reg[_src] as u64 & SHIFT_MASK_64)) as u64
- // TODO: check this actually works as expected for signed / unsigned ops
- ebpf::JA => do_jump(&mut insn_ptr, &insn),
- if reg[_dst] == insn.imm as u64 {
- do_jump(&mut insn_ptr, &insn);
- if reg[_dst] == reg[_src] {
- if reg[_dst] > insn.imm as u64 {
- if reg[_dst] > reg[_src] {
- if reg[_dst] >= insn.imm as u64 {
- if reg[_dst] >= reg[_src] {
- if reg[_dst] < insn.imm as u64 {
- if reg[_dst] < reg[_src] {
- if reg[_dst] <= insn.imm as u64 {
- if reg[_dst] <= reg[_src] {
- if reg[_dst] & insn.imm as u64 != 0 {
- if reg[_dst] & reg[_src] != 0 {
- if reg[_dst] != insn.imm as u64 {
- if reg[_dst] != reg[_src] {
- if reg[_dst] as i64 > insn.imm as i64 {
- if reg[_dst] as i64 > reg[_src] as i64 {
- if reg[_dst] as i64 >= insn.imm as i64 {
- if reg[_dst] as i64 >= reg[_src] as i64 {
- if (reg[_dst] as i64) < insn.imm as i64 {
- if (reg[_dst] as i64) < reg[_src] as i64 {
- if reg[_dst] as i64 <= insn.imm as i64 {
- if reg[_dst] as i64 <= reg[_src] as i64 {
- if reg[_dst] as u32 == insn.imm as u32 {
- if reg[_dst] as u32 == reg[_src] as u32 {
- if reg[_dst] as u32 > insn.imm as u32 {
- if reg[_dst] as u32 > reg[_src] as u32 {
- if reg[_dst] as u32 >= insn.imm as u32 {
- if reg[_dst] as u32 >= reg[_src] as u32 {
- if (reg[_dst] as u32) < insn.imm as u32 {
- if (reg[_dst] as u32) < reg[_src] as u32 {
- if reg[_dst] as u32 <= insn.imm as u32 {
- if reg[_dst] as u32 <= reg[_src] as u32 {
- if reg[_dst] as u32 & insn.imm as u32 != 0 {
- if reg[_dst] as u32 & reg[_src] as u32 != 0 {
- if reg[_dst] as u32 != insn.imm as u32 {
- if reg[_dst] as u32 != reg[_src] as u32 {
- if reg[_dst] as i32 > insn.imm {
- if reg[_dst] as i32 > reg[_src] as i32 {
- if reg[_dst] as i32 >= insn.imm {
- if reg[_dst] as i32 >= reg[_src] as i32 {
- if (reg[_dst] as i32) < insn.imm {
- if (reg[_dst] as i32) < reg[_src] as i32 {
- if reg[_dst] as i32 <= insn.imm {
- if reg[_dst] as i32 <= reg[_src] as i32 {
- // See https://www.kernel.org/doc/html/latest/bpf/standardization/instruction-set.html#id16
- let src_reg = _src;
- let call_func_res = match src_reg {
- 0 => {
- // Handle call by address to external function.
- if let Some(function) = helpers.get(&(insn.imm as u32)) {
- reg[0] = function(reg[1], reg[2], reg[3], reg[4], reg[5]);
- }else {
- Err(format!(
- "Error: unknown helper function (id: {:#x}) [{}], (instruction #{})",
- insn.imm as u32,BPF_FUNC_MAPPER[insn.imm as usize],insn_ptr
- 1 => {
- // bpf to bpf call
- // The function is in the same program, so we can just jump to the address
- if stacks.len() >= ebpf::RBPF_MAX_CALL_DEPTH{
- "Error: bpf to bpf call stack limit reached (instruction #{}) max depth: {}",
- insn_ptr, ebpf::RBPF_MAX_CALL_DEPTH
- let mut pre_stack = stacks.last_mut().unwrap();
- // Save the callee saved registers
- pre_stack.save_registers(®[6..=9]);
- // Save the return address
- pre_stack.save_return_address(insn_ptr as u64);
- // save the stack pointer
- pre_stack.save_sp(reg[10]);
- let mut stack = StackFrame::new();
- log::trace!("BPF TO BPF CALL: new pc: {} + {} = {}",insn_ptr ,insn.imm,insn_ptr + insn.imm as usize);
- reg[10] = stack.as_ptr() as u64 + stack.len() as u64;
- insn_ptr += insn.imm as usize;
- _ =>{
- "Error: the function call type (id: {:#x}) [{}], (instruction #{}) not supported",
- if let Err(e) = call_func_res {
- Err(Error::new(ErrorKind::Other, e))?;
- if stacks.len() == 1 {
- return Ok(reg[0]);
- // Pop the stack
- stacks.pop();
- let stack = stacks.last().unwrap();
- // Restore the callee saved registers
- reg[6..=9].copy_from_slice(&stack.get_registers());
- // Restore the return address
- insn_ptr = stack.get_return_address() as usize;
- // Restore the stack pointer
- reg[10] = stack.get_sp();
- log::trace!("EXIT: new pc: {}", insn_ptr);
- unreachable!()
@@ -1,1054 +0,0 @@
-// (uBPF: JIT algorithm, originally in C)
-// (Translation to Rust, MetaBuff addition)
-use std::{
- fmt::{Error as FormatterError, Formatter},
- io::{Error, ErrorKind},
- mem,
- ops::{Index, IndexMut},
-use crate::{ebpf, HashMap};
-extern crate libc;
-type MachineCode = unsafe fn(*mut u8, usize, *mut u8, usize, usize, usize) -> u64;
-const PAGE_SIZE: usize = 4096;
-// TODO: check how long the page must be to be sure to support an eBPF program of maximum possible
-// length
-const NUM_PAGES: usize = 1;
-// Special values for target_pc in struct Jump
-const TARGET_OFFSET: isize = ebpf::PROG_MAX_INSNS as isize;
-const TARGET_PC_EXIT: isize = TARGET_OFFSET + 1;
-enum OperandSize {
- S8 = 8,
- S16 = 16,
- S32 = 32,
- S64 = 64,
-// Registers
-const RAX: u8 = 0;
-const RCX: u8 = 1;
-const RDX: u8 = 2;
-const RBX: u8 = 3;
-const RSP: u8 = 4;
-const RBP: u8 = 5;
-const RSI: u8 = 6;
-const RDI: u8 = 7;
-const R8: u8 = 8;
-const R9: u8 = 9;
-const R10: u8 = 10;
-const R11: u8 = 11;
-//const R12: u8 = 12;
-const R13: u8 = 13;
-const R14: u8 = 14;
-const R15: u8 = 15;
-const REGISTER_MAP_SIZE: usize = 11;
-const REGISTER_MAP: [u8; REGISTER_MAP_SIZE] = [
- RAX, // 0 return value
- RDI, // 1 arg 1
- RSI, // 2 arg 2
- RDX, // 3 arg 3
- R9, // 4 arg 4
- R8, // 5 arg 5
- RBX, // 6 callee-saved
- R13, // 7 callee-saved
- R14, // 8 callee-saved
- R15, // 9 callee-saved
- RBP, // 10 stack pointer
- // R10 and R11 are used to compute store a constant pointer to mem and to compute offset for
- // LD_ABS_* and LD_IND_* operations, so they are not mapped to any eBPF register.
-// Return the x86 register for the given eBPF register
-fn map_register(r: u8) -> u8 {
- assert!(r < REGISTER_MAP_SIZE as u8);
- REGISTER_MAP[(r % REGISTER_MAP_SIZE as u8) as usize]
-macro_rules! emit_bytes {
- ( $mem:ident, $data:tt, $t:ty ) => {{
- let size = mem::size_of::<$t>() as usize;
- assert!($mem.offset + size <= $mem.contents.len());
- let mut ptr = $mem.contents.as_ptr().add($mem.offset) as *mut $t;
- ptr.write_unaligned($data);
- $mem.offset += size;
- }};
-#[derive(Debug)]
-struct Jump {
- offset_loc: usize,
- target_pc: isize,
-struct JitCompiler {
- pc_locs: Vec<usize>,
- special_targets: HashMap<isize, usize>,
- jumps: Vec<Jump>,
-impl JitCompiler {
- fn new() -> JitCompiler {
- JitCompiler {
- pc_locs: vec![],
- jumps: vec![],
- special_targets: HashMap::new(),
- fn emit1(&self, mem: &mut JitMemory, data: u8) {
- emit_bytes!(mem, data, u8);
- fn emit2(&self, mem: &mut JitMemory, data: u16) {
- emit_bytes!(mem, data, u16);
- fn emit4(&self, mem: &mut JitMemory, data: u32) {
- emit_bytes!(mem, data, u32);
- fn emit8(&self, mem: &mut JitMemory, data: u64) {
- emit_bytes!(mem, data, u64);
- fn emit_modrm(&self, mem: &mut JitMemory, modrm: u8, r: u8, m: u8) {
- assert_eq!((modrm | 0xc0), 0xc0);
- self.emit1(mem, (modrm & 0xc0) | ((r & 0b111) << 3) | (m & 0b111));
- fn emit_modrm_reg2reg(&self, mem: &mut JitMemory, r: u8, m: u8) {
- self.emit_modrm(mem, 0xc0, r, m);
- fn emit_modrm_and_displacement(&self, mem: &mut JitMemory, r: u8, m: u8, d: i32) {
- if d == 0 && (m & 0b111) != RBP {
- self.emit_modrm(mem, 0x00, r, m);
- } else if (-128..=127).contains(&d) {
- self.emit_modrm(mem, 0x40, r, m);
- self.emit1(mem, d as u8);
- self.emit_modrm(mem, 0x80, r, m);
- self.emit4(mem, d as u32);
- fn basix_rex_would_set_bits(&self, w: u8, src: u8, dst: u8) -> bool {
- w != 0 || (src & 0b1000) != 0 || (dst & 0b1000) != 0
- fn emit_rex(&self, mem: &mut JitMemory, w: u8, r: u8, x: u8, b: u8) {
- assert_eq!((w | 1), 1);
- assert_eq!((r | 1), 1);
- assert_eq!((x | 1), 1);
- assert_eq!((b | 1), 1);
- self.emit1(mem, 0x40 | (w << 3) | (r << 2) | (x << 1) | b);
- // Emits a REX prefix with the top bit of src and dst.
- // Skipped if no bits would be set.
- fn emit_basic_rex(&self, mem: &mut JitMemory, w: u8, src: u8, dst: u8) {
- if self.basix_rex_would_set_bits(w, src, dst) {
- let is_masked = |val, mask| match val & mask {
- 0 => 0,
- self.emit_rex(mem, w, is_masked(src, 8), 0, is_masked(dst, 8));
- fn emit_push(&self, mem: &mut JitMemory, r: u8) {
- self.emit_basic_rex(mem, 0, 0, r);
- self.emit1(mem, 0x50 | (r & 0b111));
- fn emit_pop(&self, mem: &mut JitMemory, r: u8) {
- self.emit1(mem, 0x58 | (r & 0b111));
- // REX prefix and ModRM byte
- // We use the MR encoding when there is a choice
- // 'src' is often used as an opcode extension
- fn emit_alu32(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8) {
- self.emit_basic_rex(mem, 0, src, dst);
- self.emit1(mem, op);
- self.emit_modrm_reg2reg(mem, src, dst);
- // REX prefix, ModRM byte, and 32-bit immediate
- fn emit_alu32_imm32(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i32) {
- self.emit_alu32(mem, op, src, dst);
- self.emit4(mem, imm as u32);
- // REX prefix, ModRM byte, and 8-bit immediate
- fn emit_alu32_imm8(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i8) {
- self.emit1(mem, imm as u8);
- // REX.W prefix and ModRM byte
- fn emit_alu64(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8) {
- self.emit_basic_rex(mem, 1, src, dst);
- // REX.W prefix, ModRM byte, and 32-bit immediate
- fn emit_alu64_imm32(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i32) {
- self.emit_alu64(mem, op, src, dst);
- // REX.W prefix, ModRM byte, and 8-bit immediate
- fn emit_alu64_imm8(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i8) {
- // Register to register mov
- fn emit_mov(&self, mem: &mut JitMemory, src: u8, dst: u8) {
- self.emit_alu64(mem, 0x89, src, dst);
- fn emit_cmp_imm32(&self, mem: &mut JitMemory, dst: u8, imm: i32) {
- self.emit_alu64_imm32(mem, 0x81, 7, dst, imm);
- fn emit_cmp(&self, mem: &mut JitMemory, src: u8, dst: u8) {
- self.emit_alu64(mem, 0x39, src, dst);
- fn emit_cmp32_imm32(&self, mem: &mut JitMemory, dst: u8, imm: i32) {
- self.emit_alu32_imm32(mem, 0x81, 7, dst, imm);
- fn emit_cmp32(&self, mem: &mut JitMemory, src: u8, dst: u8) {
- self.emit_alu32(mem, 0x39, src, dst);
- // Load [src + offset] into dst
- fn emit_load(&self, mem: &mut JitMemory, size: OperandSize, src: u8, dst: u8, offset: i32) {
- let data = match size {
- OperandSize::S64 => 1,
- _ => 0,
- self.emit_basic_rex(mem, data, dst, src);
- match size {
- OperandSize::S8 => {
- // movzx
- self.emit1(mem, 0x0f);
- self.emit1(mem, 0xb6);
- OperandSize::S16 => {
- self.emit1(mem, 0xb7);
- OperandSize::S32 | OperandSize::S64 => {
- // mov
- self.emit1(mem, 0x8b);
- self.emit_modrm_and_displacement(mem, dst, src, offset);
- // Load sign-extended immediate into register
- fn emit_load_imm(&self, mem: &mut JitMemory, dst: u8, imm: i64) {
- if imm >= i32::MIN as i64 && imm <= i32::MAX as i64 {
- self.emit_alu64_imm32(mem, 0xc7, 0, dst, imm as i32);
- // movabs $imm,dst
- self.emit_basic_rex(mem, 1, 0, dst);
- self.emit1(mem, 0xb8 | (dst & 0b111));
- self.emit8(mem, imm as u64);
- // Store register src to [dst + offset]
- fn emit_store(&self, mem: &mut JitMemory, size: OperandSize, src: u8, dst: u8, offset: i32) {
- OperandSize::S16 => self.emit1(mem, 0x66), // 16-bit override
- let (is_s8, is_u64, rexw) = match size {
- OperandSize::S8 => (true, false, 0),
- OperandSize::S64 => (false, true, 1),
- _ => (false, false, 0),
- if is_u64 || (src & 0b1000) != 0 || (dst & 0b1000) != 0 || is_s8 {
- self.emit_rex(mem, rexw, is_masked(src, 8), 0, is_masked(dst, 8));
- OperandSize::S8 => self.emit1(mem, 0x88),
- _ => self.emit1(mem, 0x89),
- self.emit_modrm_and_displacement(mem, src, dst, offset);
- // Store immediate to [dst + offset]
- fn emit_store_imm32(
- mem: &mut JitMemory,
- size: OperandSize,
- dst: u8,
- offset: i32,
- imm: i32,
- OperandSize::S64 => self.emit_basic_rex(mem, 1, 0, dst),
- _ => self.emit_basic_rex(mem, 0, 0, dst),
- OperandSize::S8 => self.emit1(mem, 0xc6),
- _ => self.emit1(mem, 0xc7),
- self.emit_modrm_and_displacement(mem, 0, dst, offset);
- OperandSize::S8 => self.emit1(mem, imm as u8),
- OperandSize::S16 => self.emit2(mem, imm as u16),
- _ => self.emit4(mem, imm as u32),
- fn emit_direct_jcc(&self, mem: &mut JitMemory, code: u8, offset: u32) {
- self.emit1(mem, code);
- emit_bytes!(mem, offset, u32);
- fn emit_call(&self, mem: &mut JitMemory, target: usize) {
- // TODO use direct call when possible
- self.emit_load_imm(mem, RAX, target as i64);
- // callq *%rax
- self.emit1(mem, 0xff);
- self.emit1(mem, 0xd0);
- fn emit_jump_offset(&mut self, mem: &mut JitMemory, target_pc: isize) {
- let jump = Jump {
- offset_loc: mem.offset,
- target_pc,
- self.jumps.push(jump);
- self.emit4(mem, 0);
- fn emit_jcc(&mut self, mem: &mut JitMemory, code: u8, target_pc: isize) {
- self.emit_jump_offset(mem, target_pc);
- fn emit_jmp(&mut self, mem: &mut JitMemory, target_pc: isize) {
- self.emit1(mem, 0xe9);
- fn set_anchor(&mut self, mem: &mut JitMemory, target: isize) {
- self.special_targets.insert(target, mem.offset);
- fn emit_muldivmod(
- pc: u16,
- opc: u8,
- src: u8,
- let mul = (opc & ebpf::BPF_ALU_OP_MASK) == (ebpf::MUL32_IMM & ebpf::BPF_ALU_OP_MASK);
- let div = (opc & ebpf::BPF_ALU_OP_MASK) == (ebpf::DIV32_IMM & ebpf::BPF_ALU_OP_MASK);
- let modrm = (opc & ebpf::BPF_ALU_OP_MASK) == (ebpf::MOD32_IMM & ebpf::BPF_ALU_OP_MASK);
- let is64 = (opc & ebpf::BPF_CLS_MASK) == ebpf::BPF_ALU64;
- let is_reg = (opc & ebpf::BPF_X) == ebpf::BPF_X;
- if (div || mul) && !is_reg && imm == 0 {
- // Division by zero returns 0
- // Set register to 0: xor with itself
- self.emit_alu32(mem, 0x31, dst, dst);
- if modrm && !is_reg && imm == 0 {
- // Modulo remainder of division by zero keeps destination register unchanged
- if (div || modrm) && is_reg {
- self.emit_load_imm(mem, RCX, pc as i64);
- // test src,src
- if is64 {
- self.emit_alu64(mem, 0x85, src, src);
- self.emit_alu32(mem, 0x85, src, src);
- if div {
- // No division by 0: skip next instructions
- // Jump offset: emit_alu32 adds 2 to 3 bytes, emit_jmp adds 5
- let offset = match self.basix_rex_would_set_bits(0, dst, dst) {
- true => 3 + 5,
- false => 2 + 5,
- self.emit_direct_jcc(mem, 0x85, offset);
- // Division by 0: set dst to 0 then go to next instruction
- self.emit_jmp(mem, (pc + 1) as isize);
- if modrm {
- // Modulo by zero: keep destination register unchanged
- self.emit_jcc(mem, 0x84, (pc + 1) as isize);
- if dst != RAX {
- self.emit_push(mem, RAX);
- if dst != RDX {
- self.emit_push(mem, RDX);
- if imm != 0 {
- self.emit_load_imm(mem, RCX, imm as i64);
- self.emit_mov(mem, src, RCX);
- self.emit_mov(mem, dst, RAX);
- if div || modrm {
- // Set register to 0: xor %edx,%edx
- self.emit_alu32(mem, 0x31, RDX, RDX);
- self.emit_rex(mem, 1, 0, 0, 0);
- // mul %ecx or div %ecx
- self.emit_alu32(mem, 0xf7, if mul { 4 } else { 6 }, RCX);
- self.emit_mov(mem, RDX, dst);
- self.emit_pop(mem, RDX);
- if div || mul {
- self.emit_mov(mem, RAX, dst);
- self.emit_pop(mem, RAX);
- fn jit_compile(
- prog: &[u8],
- use_mbuff: bool,
- update_data_ptr: bool,
- self.emit_push(mem, RBP);
- self.emit_push(mem, RBX);
- self.emit_push(mem, R13);
- self.emit_push(mem, R14);
- self.emit_push(mem, R15);
- // RDI: mbuff
- // RSI: mbuff_len
- // RDX: mem
- // RCX: mem_len
- // R8: mem_offset
- // R9: mem_end_offset
- // Save mem pointer for use with LD_ABS_* and LD_IND_* instructions
- self.emit_mov(mem, RDX, R10);
- match (use_mbuff, update_data_ptr) {
- (false, _) => {
- // We do not use any mbuff. Move mem pointer into register 1.
- if map_register(1) != RDX {
- self.emit_mov(mem, RDX, map_register(1));
- (true, false) => {
- // We use a mbuff already pointing to mem and mem_end: move it to register 1.
- if map_register(1) != RDI {
- self.emit_mov(mem, RDI, map_register(1));
- (true, true) => {
- // We have a fixed (simulated) mbuff: update mem and mem_end offset values in it.
- // Store mem at mbuff + mem_offset. Trash R8.
- self.emit_alu64(mem, 0x01, RDI, R8); // add mbuff to mem_offset in R8
- self.emit_store(mem, OperandSize::S64, RDX, R8, 0); // set mem at mbuff + mem_offset
- // Store mem_end at mbuff + mem_end_offset. Trash R9.
- self.emit_load(mem, OperandSize::S64, RDX, R8, 0); // load mem into R8
- self.emit_alu64(mem, 0x01, RCX, R8); // add mem_len to mem (= mem_end)
- self.emit_alu64(mem, 0x01, RDI, R9); // add mbuff to mem_end_offset
- self.emit_store(mem, OperandSize::S64, R8, R9, 0); // store mem_end
- // Move rdi into register 1
- // Copy stack pointer to R10
- self.emit_mov(mem, RSP, map_register(10));
- // Allocate stack space
- self.emit_alu64_imm32(mem, 0x81, 5, RSP, ebpf::STACK_SIZE as i32);
- self.pc_locs = vec![0; prog.len() / ebpf::INSN_SIZE + 1];
- self.pc_locs[insn_ptr] = mem.offset;
- let dst = map_register(insn.dst);
- let src = map_register(insn.src);
- let target_pc = insn_ptr as isize + insn.off as isize + 1;
- // R10 is a constant pointer to mem.
- ebpf::LD_ABS_B => self.emit_load(mem, OperandSize::S8, R10, RAX, insn.imm),
- ebpf::LD_ABS_H => self.emit_load(mem, OperandSize::S16, R10, RAX, insn.imm),
- ebpf::LD_ABS_W => self.emit_load(mem, OperandSize::S32, R10, RAX, insn.imm),
- ebpf::LD_ABS_DW => self.emit_load(mem, OperandSize::S64, R10, RAX, insn.imm),
- self.emit_mov(mem, R10, R11); // load mem into R11
- self.emit_alu64(mem, 0x01, src, R11); // add src to R11
- self.emit_load(mem, OperandSize::S8, R11, RAX, insn.imm); // ld R0, mem[src+imm]
- self.emit_load(mem, OperandSize::S16, R11, RAX, insn.imm); // ld R0, mem[src+imm]
- self.emit_load(mem, OperandSize::S32, R11, RAX, insn.imm); // ld R0, mem[src+imm]
- self.emit_load(mem, OperandSize::S64, R11, RAX, insn.imm); // ld R0, mem[src+imm]
- let second_part = ebpf::get_insn(prog, insn_ptr).imm as u64;
- let imm = (insn.imm as u32) as u64 | second_part.wrapping_shl(32);
- self.emit_load_imm(mem, dst, imm as i64);
- ebpf::LD_B_REG => self.emit_load(mem, OperandSize::S8, src, dst, insn.off as i32),
- ebpf::LD_H_REG => self.emit_load(mem, OperandSize::S16, src, dst, insn.off as i32),
- ebpf::LD_W_REG => self.emit_load(mem, OperandSize::S32, src, dst, insn.off as i32),
- ebpf::LD_DW_REG => self.emit_load(mem, OperandSize::S64, src, dst, insn.off as i32),
- self.emit_store_imm32(mem, OperandSize::S8, dst, insn.off as i32, insn.imm)
- self.emit_store_imm32(mem, OperandSize::S16, dst, insn.off as i32, insn.imm)
- self.emit_store_imm32(mem, OperandSize::S32, dst, insn.off as i32, insn.imm)
- self.emit_store_imm32(mem, OperandSize::S64, dst, insn.off as i32, insn.imm)
- ebpf::ST_B_REG => self.emit_store(mem, OperandSize::S8, src, dst, insn.off as i32),
- ebpf::ST_H_REG => self.emit_store(mem, OperandSize::S16, src, dst, insn.off as i32),
- ebpf::ST_W_REG => self.emit_store(mem, OperandSize::S32, src, dst, insn.off as i32),
- self.emit_store(mem, OperandSize::S64, src, dst, insn.off as i32)
- ebpf::ADD32_IMM => self.emit_alu32_imm32(mem, 0x81, 0, dst, insn.imm),
- ebpf::ADD32_REG => self.emit_alu32(mem, 0x01, src, dst),
- ebpf::SUB32_IMM => self.emit_alu32_imm32(mem, 0x81, 5, dst, insn.imm),
- ebpf::SUB32_REG => self.emit_alu32(mem, 0x29, src, dst),
- ebpf::MUL32_IMM
- | ebpf::MUL32_REG
- | ebpf::DIV32_IMM
- | ebpf::DIV32_REG
- | ebpf::MOD32_IMM
- | ebpf::MOD32_REG => {
- self.emit_muldivmod(mem, insn_ptr as u16, insn.opc, src, dst, insn.imm)
- ebpf::OR32_IMM => self.emit_alu32_imm32(mem, 0x81, 1, dst, insn.imm),
- ebpf::OR32_REG => self.emit_alu32(mem, 0x09, src, dst),
- ebpf::AND32_IMM => self.emit_alu32_imm32(mem, 0x81, 4, dst, insn.imm),
- ebpf::AND32_REG => self.emit_alu32(mem, 0x21, src, dst),
- ebpf::LSH32_IMM => self.emit_alu32_imm8(mem, 0xc1, 4, dst, insn.imm as i8),
- self.emit_alu32(mem, 0xd3, 4, dst);
- ebpf::RSH32_IMM => self.emit_alu32_imm8(mem, 0xc1, 5, dst, insn.imm as i8),
- self.emit_alu32(mem, 0xd3, 5, dst);
- ebpf::NEG32 => self.emit_alu32(mem, 0xf7, 3, dst),
- ebpf::XOR32_IMM => self.emit_alu32_imm32(mem, 0x81, 6, dst, insn.imm),
- ebpf::XOR32_REG => self.emit_alu32(mem, 0x31, src, dst),
- ebpf::MOV32_IMM => self.emit_alu32_imm32(mem, 0xc7, 0, dst, insn.imm),
- ebpf::MOV32_REG => self.emit_mov(mem, src, dst),
- ebpf::ARSH32_IMM => self.emit_alu32_imm8(mem, 0xc1, 7, dst, insn.imm as i8),
- self.emit_alu32(mem, 0xd3, 7, dst);
- ebpf::LE => {} // No-op
- 16 => {
- // rol
- self.emit1(mem, 0x66); // 16-bit override
- self.emit_alu32_imm8(mem, 0xc1, 0, dst, 8);
- // and
- self.emit_alu32_imm32(mem, 0x81, 4, dst, 0xffff);
- 32 | 64 => {
- // bswap
- let bit = match insn.imm {
- 64 => 1,
- self.emit_basic_rex(mem, bit, 0, dst);
- self.emit1(mem, 0xc8 | (dst & 0b111));
- _ => unreachable!(), // Should have been caught by verifier
- ebpf::ADD64_IMM => self.emit_alu64_imm32(mem, 0x81, 0, dst, insn.imm),
- ebpf::ADD64_REG => self.emit_alu64(mem, 0x01, src, dst),
- ebpf::SUB64_IMM => self.emit_alu64_imm32(mem, 0x81, 5, dst, insn.imm),
- ebpf::SUB64_REG => self.emit_alu64(mem, 0x29, src, dst),
- ebpf::MUL64_IMM
- | ebpf::MUL64_REG
- | ebpf::DIV64_IMM
- | ebpf::DIV64_REG
- | ebpf::MOD64_IMM
- | ebpf::MOD64_REG => {
- ebpf::OR64_IMM => self.emit_alu64_imm32(mem, 0x81, 1, dst, insn.imm),
- ebpf::OR64_REG => self.emit_alu64(mem, 0x09, src, dst),
- ebpf::AND64_IMM => self.emit_alu64_imm32(mem, 0x81, 4, dst, insn.imm),
- ebpf::AND64_REG => self.emit_alu64(mem, 0x21, src, dst),
- ebpf::LSH64_IMM => self.emit_alu64_imm8(mem, 0xc1, 4, dst, insn.imm as i8),
- self.emit_alu64(mem, 0xd3, 4, dst);
- ebpf::RSH64_IMM => self.emit_alu64_imm8(mem, 0xc1, 5, dst, insn.imm as i8),
- self.emit_alu64(mem, 0xd3, 5, dst);
- ebpf::NEG64 => self.emit_alu64(mem, 0xf7, 3, dst),
- ebpf::XOR64_IMM => self.emit_alu64_imm32(mem, 0x81, 6, dst, insn.imm),
- ebpf::XOR64_REG => self.emit_alu64(mem, 0x31, src, dst),
- ebpf::MOV64_IMM => self.emit_load_imm(mem, dst, insn.imm as i64),
- ebpf::MOV64_REG => self.emit_mov(mem, src, dst),
- ebpf::ARSH64_IMM => self.emit_alu64_imm8(mem, 0xc1, 7, dst, insn.imm as i8),
- self.emit_alu64(mem, 0xd3, 7, dst);
- ebpf::JA => self.emit_jmp(mem, target_pc),
- self.emit_cmp_imm32(mem, dst, insn.imm);
- self.emit_jcc(mem, 0x84, target_pc);
- self.emit_cmp(mem, src, dst);
- self.emit_jcc(mem, 0x87, target_pc);
- self.emit_jcc(mem, 0x83, target_pc);
- self.emit_jcc(mem, 0x82, target_pc);
- self.emit_jcc(mem, 0x86, target_pc);
- self.emit_alu64_imm32(mem, 0xf7, 0, dst, insn.imm);
- self.emit_jcc(mem, 0x85, target_pc);
- self.emit_alu64(mem, 0x85, src, dst);
- self.emit_jcc(mem, 0x8f, target_pc);
- self.emit_jcc(mem, 0x8d, target_pc);
- self.emit_jcc(mem, 0x8c, target_pc);
- self.emit_jcc(mem, 0x8e, target_pc);
- self.emit_cmp32_imm32(mem, dst, insn.imm);
- self.emit_cmp32(mem, src, dst);
- self.emit_alu32_imm32(mem, 0xf7, 0, dst, insn.imm);
- self.emit_alu32(mem, 0x85, src, dst);
- // For JIT, helpers in use MUST be registered at compile time. They can be
- // updated later, but not created after compiling (we need the address of the
- // helper function in the JIT-compiled program).
- if let Some(helper) = helpers.get(&(insn.imm as u32)) {
- // We reserve RCX for shifts
- self.emit_mov(mem, R9, RCX);
- self.emit_call(mem, *helper as usize);
- Err(Error::new(
- "[JIT] Error: unknown helper function (id: {:#x})",
- ))?;
- unimplemented!()
- if insn_ptr != prog.len() / ebpf::INSN_SIZE - 1 {
- self.emit_jmp(mem, TARGET_PC_EXIT);
- "[JIT] Error: unknown eBPF opcode {:#2x} (insn #{insn_ptr:?})",
- insn.opc
- // Epilogue
- self.set_anchor(mem, TARGET_PC_EXIT);
- // Move register 0 into rax
- if map_register(0) != RAX {
- self.emit_mov(mem, map_register(0), RAX);
- // Deallocate stack space
- self.emit_alu64_imm32(mem, 0x81, 0, RSP, ebpf::STACK_SIZE as i32);
- self.emit_pop(mem, R15);
- self.emit_pop(mem, R14);
- self.emit_pop(mem, R13);
- self.emit_pop(mem, RBX);
- self.emit_pop(mem, RBP);
- self.emit1(mem, 0xc3); // ret
- fn resolve_jumps(&mut self, mem: &mut JitMemory) -> Result<(), Error> {
- for jump in &self.jumps {
- let target_loc = match self.special_targets.get(&jump.target_pc) {
- Some(target) => *target,
- None => self.pc_locs[jump.target_pc as usize],
- // Assumes jump offset is at end of instruction
- let offset_loc = jump.offset_loc as i32 + std::mem::size_of::<i32>() as i32;
- let rel = &(target_loc as i32 - offset_loc) as *const i32;
- let offset_ptr = mem.contents.as_ptr().add(jump.offset_loc);
- libc::memcpy(
- offset_ptr as *mut libc::c_void,
- rel as *const libc::c_void,
- std::mem::size_of::<i32>(),
-} // impl JitCompiler
-pub struct JitMemory<'a> {
- contents: &'a mut [u8],
- offset: usize,
-impl<'a> JitMemory<'a> {
- pub fn new(
- ) -> Result<JitMemory<'a>, Error> {
- let contents: &mut [u8];
- let mut raw: mem::MaybeUninit<*mut libc::c_void> = mem::MaybeUninit::uninit();
- let size = NUM_PAGES * PAGE_SIZE;
- libc::posix_memalign(raw.as_mut_ptr(), PAGE_SIZE, size);
- libc::mprotect(
- *raw.as_mut_ptr(),
- size,
- libc::PROT_EXEC | libc::PROT_READ | libc::PROT_WRITE,
- std::ptr::write_bytes(*raw.as_mut_ptr(), 0xc3, size); // for now, prepopulate with 'RET' calls
- contents =
- std::slice::from_raw_parts_mut(*raw.as_mut_ptr() as *mut u8, NUM_PAGES * PAGE_SIZE);
- raw.assume_init();
- let mut mem = JitMemory {
- contents,
- offset: 0,
- let mut jit = JitCompiler::new();
- jit.jit_compile(&mut mem, prog, use_mbuff, update_data_ptr, helpers)?;
- jit.resolve_jumps(&mut mem)?;
- Ok(mem)
- pub fn get_prog(&self) -> MachineCode {
- unsafe { mem::transmute(self.contents.as_ptr()) }
-impl<'a> Index<usize> for JitMemory<'a> {
- type Output = u8;
- fn index(&self, _index: usize) -> &u8 {
- &self.contents[_index]
-impl<'a> IndexMut<usize> for JitMemory<'a> {
- fn index_mut(&mut self, _index: usize) -> &mut u8 {
- &mut self.contents[_index]
-impl<'a> Drop for JitMemory<'a> {
- libc::free(self.contents.as_mut_ptr() as *mut libc::c_void);
-impl<'a> std::fmt::Debug for JitMemory<'a> {
- fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatterError> {
- fmt.write_str("JIT contents: [")?;
- fmt.write_str(" ] | ")?;
- fmt.debug_struct("JIT memory")
- .field("offset", &self.offset)
- .finish()
@@ -1,1782 +0,0 @@
-// Copyright 2023 Isovalent, Inc. <quentin@isovalent.com>
-//! Virtual machine and JIT compiler for eBPF programs.
-#![doc(
- html_logo_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.png",
- html_favicon_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.ico"
-)]
-#![warn(missing_docs)]
-// There are unused mut warnings due to unsafe code.
-#![allow(unused_mut)]
-// Allows old-style clippy
-#![allow(renamed_and_removed_lints)]
-#![cfg_attr(
- clippy,
- allow(
- redundant_field_names,
- single_match,
- cast_lossless,
- doc_markdown,
- match_same_arms,
- unreadable_literal
-// Configures the crate to be `no_std` when `std` feature is disabled.
-#![cfg_attr(not(feature = "std"), no_std)]
-extern crate alloc;
-type HashMap<K, V> = BTreeMap<K, V>;
-#[cfg(feature = "cranelift")]
-type HashSet<T> = alloc::collections::BTreeSet<T>;
-mod asm_parser;
-pub mod assembler;
-mod cranelift;
-pub mod disassembler;
-pub mod ebpf;
-pub mod helpers;
-pub mod insn_builder;
-mod interpreter;
-#[cfg(all(not(windows), feature = "std"))]
-mod jit;
-#[cfg(not(feature = "std"))]
-mod no_std_error;
-mod stack;
-mod verifier;
-pub use std::io::{Error, ErrorKind};
-/// In no_std we use a custom implementation of the error which acts as a
-/// replacement for the io Error.
-pub use crate::no_std_error::{Error, ErrorKind};
-/// eBPF verification function that returns an error if the program does not meet its requirements.
-/// Some examples of things the verifier may reject the program for:
-/// - Program does not terminate.
-/// - Unknown instructions.
-/// - Bad formed instruction.
-/// - Unknown eBPF helper index.
-/// eBPF helper function.
-// A metadata buffer with two offset indications. It can be used in one kind of eBPF VM to simulate
-// the use of a metadata buffer each time the program is executed, without the user having to
-// actually handle it. The offsets are used to tell the VM where in the buffer the pointers to
-// packet data start and end should be stored each time the program is run on a new packet.
-struct MetaBuff {
- data_end_offset: usize,
- buffer: Vec<u8>,
-/// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work
-/// on a metadata buffer containing pointers to packet data.
-/// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff at offset 8 into R1.
-/// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
-/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
-/// let mem = &mut [
-/// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
-/// // Just for the example we create our metadata buffer from scratch, and we store the pointers
-/// // to packet data start and end in it.
-/// let mut mbuff = [0u8; 32];
-/// let mut data = mbuff.as_ptr().offset(8) as *mut u64;
-/// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
-/// *data = mem.as_ptr() as u64;
-/// *data_end = mem.as_ptr() as u64 + mem.len() as u64;
-/// // Instantiate a VM.
-/// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
-/// // Provide both a reference to the packet data, and to the metadata buffer.
-/// let res = vm.execute_program(mem, &mut mbuff).unwrap();
-/// assert_eq!(res, 0x2211);
-pub struct EbpfVmMbuff<'a> {
- prog: Option<&'a [u8]>,
- verifier: Verifier,
- jit: Option<jit::JitMemory<'a>>,
- cranelift_prog: Option<cranelift::CraneliftProgram>,
-impl<'a> EbpfVmMbuff<'a> {
- /// Create a new virtual machine instance, and load an eBPF program into that instance.
- /// When attempting to load the program, it passes through a simple verifier.
- /// let prog = &[
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1.
- /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit
- /// // Instantiate a VM.
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmMbuff<'a>, Error> {
- if let Some(prog) = prog {
- verifier::check(prog)?;
- Ok(EbpfVmMbuff {
- prog,
- verifier: verifier::check,
- jit: None,
- cranelift_prog: None,
- helpers: HashMap::new(),
- /// Load a new eBPF program into the virtual machine instance.
- /// let prog1 = &[
- /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
- /// let prog2 = &[
- /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap();
- /// vm.set_program(prog2).unwrap();
- pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> {
- (self.verifier)(prog)?;
- self.prog = Some(prog);
- /// Set a new verifier function. The function should return an `Error` if the program should be
- /// rejected by the virtual machine. If a program has been loaded to the VM already, the
- /// verifier is immediately run.
- /// use rbpf::{Error, ErrorKind};
- /// // Define a simple verifier function.
- /// fn verifier(prog: &[u8]) -> Result<(), Error> {
- /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
- /// if last_insn.opc != ebpf::EXIT {
- /// return Err(Error::new(ErrorKind::Other,
- /// "[Verifier] Error: program does not end with “EXIT” instruction"));
- /// }
- /// Ok(())
- /// // Change the verifier.
- /// vm.set_verifier(verifier).unwrap();
- pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> {
- if let Some(prog) = self.prog {
- verifier(prog)?;
- self.verifier = verifier;
- /// Register a built-in or user-defined helper function in order to use it later from within
- /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`.
- /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the
- /// program. You should be able to change registered helpers after compiling, but not to add
- /// new ones (i.e. with new keys).
- /// use rbpf::helpers;
- /// // This program was compiled with clang, from a C program containing the following single
- /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);`
- /// 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load 0 as u64 into r1 (That would be
- /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // replaced by tc by the address of
- /// // the format string, in the .map
- /// // section of the ELF file).
- /// 0xb7, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, // mov r2, 10
- /// 0xb7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r3, 1
- /// 0xb7, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r4, 2
- /// 0xb7, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r5, 3
- /// 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // call helper with key 6
- /// // Register a helper.
- /// // On running the program this helper will print the content of registers r3, r4 and r5 to
- /// // standard output.
- /// # #[cfg(feature = "std")]
- /// vm.register_helper(6, helpers::bpf_trace_printf).unwrap();
- pub fn register_helper(&mut self, key: u32, function: Helper) -> Result<(), Error> {
- self.helpers.insert(key, function);
- /// Execute the program loaded, with the given packet data and metadata buffer.
- /// If the program is made to be compatible with Linux kernel, it is expected to load the
- /// address of the beginning and of the end of the memory area used for packet data from the
- /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these
- /// pointers are correctly stored in the buffer.
- /// let mem = &mut [
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
- /// // Just for the example we create our metadata buffer from scratch, and we store the
- /// // pointers to packet data start and end in it.
- /// let mut mbuff = [0u8; 32];
- /// unsafe {
- /// let mut data = mbuff.as_ptr().offset(8) as *mut u64;
- /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64;
- /// *data = mem.as_ptr() as u64;
- /// *data_end = mem.as_ptr() as u64 + mem.len() as u64;
- /// // Provide both a reference to the packet data, and to the metadata buffer.
- /// let res = vm.execute_program(mem, &mut mbuff).unwrap();
- /// assert_eq!(res, 0x2211);
- pub fn execute_program(&self, mem: &[u8], mbuff: &[u8]) -> Result<u64, Error> {
- interpreter::execute_program(self.prog, mem, mbuff, &self.helpers)
- /// JIT-compile the loaded program. No argument required for this.
- /// If using helper functions, be sure to register them into the VM before calling this
- /// function.
- /// vm.jit_compile();
- pub fn jit_compile(&mut self) -> Result<(), Error> {
- let prog = match self.prog {
- self.jit = Some(jit::JitMemory::new(prog, &self.helpers, true, false)?);
- /// Execute the previously JIT-compiled program, with the given packet data and metadata
- /// buffer, in a manner very similar to `execute_program()`.
- /// # Safety
- /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime
- /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end
- /// very bad (program may segfault). It may be wise to check that the program works with the
- /// interpreter before running the JIT-compiled version of it.
- /// For this reason the function should be called from within an `unsafe` bloc.
- /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1.
- /// # #[cfg(all(not(windows), feature = "std"))]
- /// let res = vm.execute_program_jit(mem, &mut mbuff).unwrap();
- pub unsafe fn execute_program_jit(
- mem: &mut [u8],
- mbuff: &'a mut [u8],
- ) -> Result<u64, Error> {
- // If packet data is empty, do not send the address of an empty slice; send a null pointer
- // as first argument instead, as this is uBPF's behavior (empty packet should not happen
- // in the kernel; anyway the verifier would prevent the use of uninitialized registers).
- // See `mul_loop` test.
- let mem_ptr = match mem.len() {
- 0 => std::ptr::null_mut(),
- _ => mem.as_ptr() as *mut u8,
- // The last two arguments are not used in this function. They would be used if there was a
- // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len()
- // should be stored; this is what happens with struct EbpfVmFixedMbuff.
- match &self.jit {
- Some(jit) => Ok(jit.get_prog()(
- mbuff.as_ptr() as *mut u8,
- mem_ptr,
- )),
- "Error: program has not been JIT-compiled",
- /// Compile the loaded program using the Cranelift JIT.
- /// vm.cranelift_compile();
- pub fn cranelift_compile(&mut self) -> Result<(), Error> {
- use crate::cranelift::CraneliftCompiler;
- let mut compiler = CraneliftCompiler::new(self.helpers.clone());
- let program = compiler.compile_function(prog)?;
- self.cranelift_prog = Some(program);
- /// Execute the previously compiled program, with the given packet data and metadata
- /// let res = vm.execute_program_cranelift(mem, &mut mbuff).unwrap();
- pub fn execute_program_cranelift(
- 0 => core::ptr::null_mut(),
- match &self.cranelift_prog {
- Some(prog) => {
- Ok(prog.execute(mem_ptr, mem.len(), mbuff.as_ptr() as *mut u8, mbuff.len()))
- "Error: program has not been compiled with cranelift",
-/// on a metadata buffer containing pointers to packet data, but it internally handles the buffer
-/// so as to save the effort to manually handle the metadata buffer for the user.
-/// This struct implements a static internal buffer that is passed to the program. The user has to
-/// indicate the offset values at which the eBPF program expects to find the start and the end of
-/// packet data in the buffer. On calling the `execute_program()` or `execute_program_jit()` functions, the
-/// struct automatically updates the addresses in this static buffer, at the appointed offsets, for
-/// the start and the end of the packet data the program is called upon.
-/// This was compiled with clang from the following program, in C:
-/// SEC(".classifier")
-/// int classifier(struct __sk_buff *skb)
-/// void *data = (void *)(long)skb->data;
-/// void *data_end = (void *)(long)skb->data_end;
-/// // Check program is long enough.
-/// if (data + 5 > data_end)
-/// return 0;
-/// return *((char *)data + 5);
-/// Some small modifications have been brought to have it work, see comments.
-/// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0
-/// // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address.
-/// // Also, offset 0x4c had to be replace with e.g. 0x40 so as to prevent the two pointers
-/// // from overlapping in the buffer.
-/// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load pointer to mem from r1[0x40] to r2
-/// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
-/// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load ptr to mem_end from r1[0x50] to r1
-/// 0x2d, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
-/// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
-/// 0x67, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 >>= 56
-/// 0xc7, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 <<= 56 (arsh) extend byte sign to u64
-/// let mem1 = &mut [
-/// let mem2 = &mut [
-/// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
-/// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
-/// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
-/// // Provide only a reference to the packet data. We do not manage the metadata buffer.
-/// let res = vm.execute_program(mem1).unwrap();
-/// assert_eq!(res, 0xffffffffffffffdd);
-/// let res = vm.execute_program(mem2).unwrap();
-/// assert_eq!(res, 0x27);
-pub struct EbpfVmFixedMbuff<'a> {
- parent: EbpfVmMbuff<'a>,
- mbuff: MetaBuff,
-impl<'a> EbpfVmFixedMbuff<'a> {
- /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2
- /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5
- /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1
- /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions
- /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0
- /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers.
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap();
- ) -> Result<EbpfVmFixedMbuff<'a>, Error> {
- let parent = EbpfVmMbuff::new(prog)?;
- let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 };
- let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)];
- let mbuff = MetaBuff {
- data_offset,
- data_end_offset,
- buffer,
- Ok(EbpfVmFixedMbuff { parent, mbuff })
- /// At the same time, load new offsets for storing pointers to start and end of packet data in
- /// the internal metadata buffer.
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27,
- /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog1), 0, 0).unwrap();
- /// vm.set_program(prog2, 0x40, 0x50);
- /// let res = vm.execute_program(mem).unwrap();
- /// assert_eq!(res, 0x27);
- pub fn set_program(
- prog: &'a [u8],
- self.mbuff.buffer = buffer;
- self.mbuff.data_offset = data_offset;
- self.mbuff.data_end_offset = data_end_offset;
- self.parent.set_program(prog)?;
- self.parent.set_verifier(verifier)
- /// #[cfg(feature = "std")] {
- /// 0x2d, 0x12, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 6 instructions
- /// 0x71, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r1
- /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0
- /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0
- /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0
- /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0
- /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x09,
- /// // Register a helper. This helper will store the result of the square root of r1 into r0.
- /// vm.register_helper(1, helpers::sqrti);
- /// assert_eq!(res, 3);
- pub fn register_helper(
- function: fn(u64, u64, u64, u64, u64) -> u64,
- self.parent.register_helper(key, function)
- /// Execute the program loaded, with the given packet data.
- /// address of the beginning and of the end of the memory area used for packet data from some
- /// metadata buffer, which in the case of this VM is handled internally. The offsets at which
- /// the addresses should be placed should have be set at the creation of the VM.
- /// // Provide only a reference to the packet data. We do not manage the metadata buffer.
- /// assert_eq!(res, 0xdd);
- pub fn execute_program(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
- let l = self.mbuff.buffer.len();
- // Can this ever happen? Probably not, should be ensured at mbuff creation.
- if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l {
- Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}",
- l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?;
- LittleEndian::write_u64(
- &mut self.mbuff.buffer[(self.mbuff.data_offset)..],
- &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..],
- mem.as_ptr() as u64 + mem.len() as u64,
- self.parent.execute_program(mem, &self.mbuff.buffer)
- let prog = match self.parent.prog {
- self.parent.jit = Some(jit::JitMemory::new(prog, &self.parent.helpers, true, true)?);
- /// Execute the previously JIT-compiled program, with the given packet data, in a manner very
- /// similar to `execute_program()`.
- /// let res = vm.execute_program_jit(mem).unwrap();
- // This struct redefines the `execute_program_jit()` function, in order to pass the offsets
- // associated with the fixed mbuff.
- pub unsafe fn execute_program_jit(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
- match &self.parent.jit {
- self.mbuff.buffer.as_ptr() as *mut u8,
- self.mbuff.buffer.len(),
- self.mbuff.data_offset,
- self.mbuff.data_end_offset,
- let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone());
- self.parent.cranelift_prog = Some(program);
- /// let res = vm.execute_program_cranelift(mem).unwrap();
- pub fn execute_program_cranelift(&mut self, mem: &'a mut [u8]) -> Result<u64, Error> {
- match &self.parent.cranelift_prog {
- Some(prog) => Ok(prog.execute(
-/// directly on the memory area representing packet data.
-/// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
-/// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
-/// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
-/// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
-/// // Provide only a reference to the packet data.
-/// let res = vm.execute_program(mem).unwrap();
-/// assert_eq!(res, 0x22cc);
-pub struct EbpfVmRaw<'a> {
-impl<'a> EbpfVmRaw<'a> {
- /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1
- /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22
- /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1
- /// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmRaw<'a>, Error> {
- Ok(EbpfVmRaw { parent })
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog1)).unwrap();
- /// vm.set_program(prog2);
- /// assert_eq!(res, 0x22cc);
- /// 0x79, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxdw r1, r1[0x00]
- /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
- /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- /// assert_eq!(res, 0x10000000);
- /// Register a set of built-in or user-defined helper functions in order to use them later from
- /// within the eBPF program. The helpers are registered into a hashmap, so the `key` can be any
- /// `u32`.
- #[allow(clippy::type_complexity)]
- pub fn register_helper_set(
- helpers: &HashMap<u32, fn(u64, u64, u64, u64, u64) -> u64>,
- for (key, function) in helpers {
- self.parent.register_helper(*key, *function)?;
- /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27
- pub fn execute_program(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
- self.parent.execute_program(mem, &[])
- self.parent.jit = Some(jit::JitMemory::new(
- &self.parent.helpers,
- false,
- )?);
- pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
- let mut mbuff = vec![];
- self.parent.execute_program_jit(mem, &mut mbuff)
- /// Execute the previously compiled program, with the given packet data, in a manner very
- pub fn execute_program_cranelift(&self, mem: &'a mut [u8]) -> Result<u64, Error> {
- self.parent.execute_program_cranelift(mem, &mut mbuff)
-/// A virtual machine to run eBPF program. This kind of VM is used for programs that do not work
-/// with any memory area—no metadata buffer, no packet data either.
-/// 0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r1, 1
-/// 0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r2, 2
-/// 0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r3, 3
-/// 0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // mov r4, 4
-/// 0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // mov r5, 5
-/// 0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // mov r6, 6
-/// 0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, // mov r7, 7
-/// 0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // mov r8, 8
-/// 0x4f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // or r0, r5
-/// 0x47, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, // or r0, 0xa0
-/// 0x57, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, // and r0, 0xa3
-/// 0xb7, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, // mov r9, 0x91
-/// 0x5f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // and r0, r9
-/// 0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // lsh r0, 32
-/// 0x67, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, // lsh r0, 22
-/// 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // lsh r0, r8
-/// 0x77, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // rsh r0, 32
-/// 0x77, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, // rsh r0, 19
-/// 0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // rsh r0, r7
-/// 0xa7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // xor r0, 0x03
-/// 0xaf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xor r0, r2
-/// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
-/// let res = vm.execute_program().unwrap();
-/// assert_eq!(res, 0x11);
-pub struct EbpfVmNoData<'a> {
- parent: EbpfVmRaw<'a>,
-impl<'a> EbpfVmNoData<'a> {
- /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211
- /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0
- /// let vm = rbpf::EbpfVmNoData::new(Some(prog));
- pub fn new(prog: Option<&'a [u8]>) -> Result<EbpfVmNoData<'a>, Error> {
- let parent = EbpfVmRaw::new(prog)?;
- Ok(EbpfVmNoData { parent })
- /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap();
- /// let res = vm.execute_program().unwrap();
- /// assert_eq!(res, 0x1122);
- /// 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // mov r1, 0x010000000
- /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- /// vm.register_helper(1, helpers::sqrti).unwrap();
- /// assert_eq!(res, 0x1000);
- self.parent.jit_compile()
- /// Execute the program loaded, without providing pointers to any memory area whatsoever.
- /// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
- /// // For this kind of VM, the `execute_program()` function needs no argument.
- pub fn execute_program(&self) -> Result<u64, Error> {
- self.parent.execute_program(&mut [])
- /// Execute the previously JIT-compiled program, without providing pointers to any memory area
- /// whatsoever, in a manner very similar to `execute_program()`.
- /// let res = vm.execute_program_jit().unwrap();
- pub unsafe fn execute_program_jit(&self) -> Result<u64, Error> {
- self.parent.execute_program_jit(&mut [])
- self.parent.cranelift_compile()
- /// let res = vm.execute_program_cranelift().unwrap();
- pub fn execute_program_cranelift(&self) -> Result<u64, Error> {
- self.parent.execute_program_cranelift(&mut [])
-/// EbpfVm with Owned data
-pub struct EbpfVmRawOwned {
- parent: EbpfVmRaw<'static>,
- data_len: usize,
- data_cap: usize,
-impl EbpfVmRawOwned {
- pub fn new(prog: Option<Vec<u8>>) -> Result<EbpfVmRawOwned, Error> {
- let (prog, data_len, data_cap) = match prog {
- let data_len = prog.len();
- let data_cap = prog.capacity();
- let slice = prog.leak();
- let slice = unsafe { core::slice::from_raw_parts(slice.as_ptr(), data_len) };
- (Some(slice), data_len, data_cap)
- None => (None, 0, 0),
- Ok(Self {
- parent,
- data_len,
- data_cap,
- /// Load a new eBPF program into the virtual machine instance
- pub fn set_program(&mut self, prog: Vec<u8>) -> Result<(), Error> {
- self.data_len = prog.len();
- self.data_cap = prog.capacity();
- self.parent.set_program(slice)?;
- /// Set a new verifier function. The function should return an Error if the program should be rejected by the virtual machine.
- /// If a program has been loaded to the VM already, the verifier is immediately run.
- /// Register a built-in or user-defined helper function in order to use it later from within the eBPF program.
- /// The helper is registered into a hashmap, so the key can be any u32.
- /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the program.
- /// You should be able to change registered helpers after compiling, but not to add new ones (i. e. with new keys).
- /// Execute the previously JIT-compiled program, with the given packet data, in a manner very similar to execute_program().
- /// Safety
- /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime check for memory access;
- /// so if the eBPF program attempts erroneous accesses, this may end very bad (program may segfault).
- /// It may be wise to check that the program works with the interpreter before running the JIT-compiled version of it.
- /// For this reason the function should be called from within an unsafe bloc.
- pub fn execute_program(&self, mem: &mut [u8]) -> Result<u64, Error> {
- self.parent.execute_program(mem)
-impl Drop for EbpfVmRawOwned {
- match self.parent.parent.prog {
- Some(prog) => unsafe {
- let ptr = prog.as_ptr();
- let _prog = Vec::from_raw_parts(ptr as *mut u8, self.data_len, self.data_cap);
- None => {}
@@ -1,41 +0,0 @@
-//! This module provides a simple implementation of the Error struct that is
-//! used as a drop-in replacement for `std::io::Error` when using `rbpf` in `no_std`.
-use alloc::string::String;
-/// Implementation of Error for no_std applications.
-/// Ensures that the existing code can use it with the same interface
-/// as the Error from std::io::Error.
-pub struct Error {
- #[allow(dead_code)]
- kind: ErrorKind,
- error: String,
-impl Error {
- /// New function exposing the same signature as `std::io::Error::new`.
- pub fn new<S: Into<String>>(kind: ErrorKind, error: S) -> Error {
- Error {
- kind,
- error: error.into(),
-/// The current version of `rbpf` only uses the [`Other`](ErrorKind::Other) variant
-/// from the [std::io::ErrorKind] enum. If a dependency on other variants were
-/// introduced in the future, this enum needs to be updated accordingly to maintain
-/// compatibility with the real `ErrorKind`. The reason all available variants
-/// aren't included in the first place is that [std::io::ErrorKind] exposes
-/// 40 variants, and not all of them are meaningful under `no_std`.
-pub enum ErrorKind {
- /// The no_std code only uses this variant.
- Other,
@@ -1,75 +0,0 @@
-use crate::{ebpf::STACK_SIZE, vec, Vec};
-pub struct StackFrame {
- return_address: u64,
- saved_registers: [u64; 4],
- sp: u64,
- frame: Vec<u8>,
-impl StackFrame {
- /// Create a new stack frame
- /// The stack frame is created with a capacity of `STACK_SIZE` == 512 bytes
- sp: 0,
- return_address: 0,
- saved_registers: [0; 4],
- frame: vec![0; STACK_SIZE],
- /// Create a new stack frame with a given capacity
- #[allow(unused)]
- pub fn with_capacity(capacity: usize) -> Self {
- frame: vec![0; capacity],
- /// The capacity of the stack frame
- pub fn len(&self) -> usize {
- self.frame.len()
- pub fn as_ptr(&self) -> *const u8 {
- self.frame.as_ptr()
- pub fn as_slice(&self) -> &[u8] {
- self.frame.as_slice()
- /// Save the callee-saved registers
- pub fn save_registers(&mut self, regs: &[u64]) {
- self.saved_registers.copy_from_slice(regs);
- /// Get the callee-saved registers
- pub fn get_registers(&self) -> [u64; 4] {
- self.saved_registers
- /// Save the return address
- pub fn save_return_address(&mut self, address: u64) {
- self.return_address = address;
- /// Get the return address
- pub fn get_return_address(&self) -> u64 {
- self.return_address
- /// Save the stack pointer
- pub fn save_sp(&mut self, sp: u64) {
- self.sp = sp;
- /// Get the stack pointer
- pub fn get_sp(&self) -> u64 {
- self.sp
@@ -1,386 +0,0 @@
-// (uBPF: safety checks, originally in C)
-// (Translation to Rust)
-// This “verifier” performs simple checks when the eBPF program is loaded into the VM (before it is
-// interpreted or JIT-compiled). It has nothing to do with the much more elaborated verifier inside
-// Linux kernel. There is no verification regarding the program flow control (should be a Direct
-// Acyclic Graph) or the consistency for registers usage (the verifier of the kernel assigns types
-// to the registers and is much stricter).
-// On the other hand, rbpf is not expected to run in kernel space.
-// Improving the verifier would be nice, but this is not trivial (and Linux kernel is under GPL
-// license, so we cannot copy it).
-// Contrary to the verifier of the Linux kernel, this one does not modify the bytecode at all.
-use alloc::format;
-use crate::{ebpf, Error, ErrorKind};
-fn reject<S: AsRef<str>>(msg: S) -> Result<(), Error> {
- let full_msg = format!("[Verifier] Error: {}", msg.as_ref());
- Err(Error::new(ErrorKind::Other, full_msg))
-fn check_prog_len(prog: &[u8]) -> Result<(), Error> {
- reject(format!(
- "eBPF program length must be a multiple of {:?} octets",
- if prog.len() > ebpf::PROG_MAX_SIZE {
- "eBPF program length limited to {:?}, here {:?}",
- ebpf::PROG_MAX_INSNS,
- prog.len() / ebpf::INSN_SIZE
- reject("no program set, call set_program() to load one")?;
- let last_opc = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1).opc;
- if last_opc & ebpf::BPF_CLS_MASK != ebpf::BPF_JMP {
- reject("program does not end with “EXIT” instruction")?;
-fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Error> {
- 16 | 32 | 64 => Ok(()),
- _ => reject(format!(
- "unsupported argument for LE/BE (insn #{insn_ptr:?})"
-fn check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), Error> {
- // We know we can reach next insn since we enforce an EXIT insn at the end of program, while
- // this function should be called only for LD_DW insn, that cannot be last in program.
- let next_insn = ebpf::get_insn(prog, insn_ptr + 1);
- if next_insn.opc != 0 {
- reject(format!("incomplete LD_DW instruction (insn #{insn_ptr:?})"))?;
-fn check_jmp_offset(prog: &[u8], insn_ptr: usize) -> Result<(), Error> {
- if insn.off == -1 {
- reject(format!("infinite loop (insn #{insn_ptr:?})"))?;
- let dst_insn_ptr = insn_ptr as isize + 1 + insn.off as isize;
- if dst_insn_ptr < 0 || dst_insn_ptr as usize >= (prog.len() / ebpf::INSN_SIZE) {
- "jump out of code to #{dst_insn_ptr:?} (insn #{insn_ptr:?})"
- let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize);
- if dst_insn.opc == 0 {
- "jump to middle of LD_DW at #{dst_insn_ptr:?} (insn #{insn_ptr:?})"
-fn check_registers(insn: &ebpf::Insn, store: bool, insn_ptr: usize) -> Result<(), Error> {
- if insn.src > 10 {
- reject(format!("invalid source register (insn #{insn_ptr:?})"))?;
- match (insn.dst, store) {
- (0..=9, _) | (10, true) => Ok(()),
- (10, false) => reject(format!(
- "cannot write into register r10 (insn #{insn_ptr:?})"
- (_, _) => reject(format!("invalid destination register (insn #{insn_ptr:?})")),
-pub fn check(prog: &[u8]) -> Result<(), Error> {
- check_prog_len(prog)?;
- let mut store = false;
- ebpf::LD_ABS_B => {}
- ebpf::LD_ABS_H => {}
- ebpf::LD_ABS_W => {}
- ebpf::LD_ABS_DW => {}
- ebpf::LD_IND_B => {}
- ebpf::LD_IND_H => {}
- ebpf::LD_IND_W => {}
- ebpf::LD_IND_DW => {}
- store = true;
- check_load_dw(prog, insn_ptr)?;
- ebpf::LD_B_REG => {}
- ebpf::LD_H_REG => {}
- ebpf::LD_W_REG => {}
- ebpf::LD_DW_REG => {}
- ebpf::ST_B_IMM => store = true,
- ebpf::ST_H_IMM => store = true,
- ebpf::ST_W_IMM => store = true,
- ebpf::ST_DW_IMM => store = true,
- ebpf::ST_B_REG => store = true,
- ebpf::ST_H_REG => store = true,
- ebpf::ST_W_REG => store = true,
- ebpf::ST_DW_REG => store = true,
- unimplemented!();
- ebpf::ADD32_IMM => {}
- ebpf::ADD32_REG => {}
- ebpf::SUB32_IMM => {}
- ebpf::SUB32_REG => {}
- ebpf::MUL32_IMM => {}
- ebpf::MUL32_REG => {}
- ebpf::DIV32_IMM => {}
- ebpf::DIV32_REG => {}
- ebpf::OR32_IMM => {}
- ebpf::OR32_REG => {}
- ebpf::AND32_IMM => {}
- ebpf::AND32_REG => {}
- ebpf::LSH32_IMM => {}
- ebpf::LSH32_REG => {}
- ebpf::RSH32_IMM => {}
- ebpf::RSH32_REG => {}
- ebpf::NEG32 => {}
- ebpf::MOD32_IMM => {}
- ebpf::MOD32_REG => {}
- ebpf::XOR32_IMM => {}
- ebpf::XOR32_REG => {}
- ebpf::MOV32_IMM => {}
- ebpf::MOV32_REG => {}
- ebpf::ARSH32_IMM => {}
- ebpf::ARSH32_REG => {}
- check_imm_endian(&insn, insn_ptr)?;
- ebpf::ADD64_IMM => {}
- ebpf::ADD64_REG => {}
- ebpf::SUB64_IMM => {}
- ebpf::SUB64_REG => {}
- ebpf::MUL64_IMM => {}
- ebpf::MUL64_REG => {}
- ebpf::DIV64_IMM => {}
- ebpf::DIV64_REG => {}
- ebpf::OR64_IMM => {}
- ebpf::OR64_REG => {}
- ebpf::AND64_IMM => {}
- ebpf::AND64_REG => {}
- ebpf::LSH64_IMM => {}
- ebpf::LSH64_REG => {}
- ebpf::RSH64_IMM => {}
- ebpf::RSH64_REG => {}
- ebpf::NEG64 => {}
- ebpf::MOD64_IMM => {}
- ebpf::MOD64_REG => {}
- ebpf::XOR64_IMM => {}
- ebpf::XOR64_REG => {}
- ebpf::MOV64_IMM => {}
- ebpf::MOV64_REG => {}
- ebpf::ARSH64_IMM => {}
- ebpf::ARSH64_REG => {}
- check_jmp_offset(prog, insn_ptr)?;
- ebpf::CALL => {}
- ebpf::EXIT => {}
- "unknown eBPF opcode {:#2x} (insn #{insn_ptr:?})",
- check_registers(&insn, store, insn_ptr)?;
- // insn_ptr should now be equal to number of instructions.
- if insn_ptr != prog.len() / ebpf::INSN_SIZE {
- reject(format!("jumped out of code to #{insn_ptr:?}"))?;
@@ -1,655 +0,0 @@
-mod common;
-use common::{TCP_SACK_ASM, TCP_SACK_BIN};
-use rbpf::{assembler::assemble, ebpf};
-fn asm(src: &str) -> Result<Vec<ebpf::Insn>, String> {
- Ok(ebpf::to_insn_vec(&(assemble(src))?))
-fn insn(opc: u8, dst: u8, src: u8, off: i16, imm: i32) -> ebpf::Insn {
- ebpf::Insn {
- dst,
- src,
- off,
-#[test]
-fn test_empty() {
- assert_eq!(asm(""), Ok(vec![]));
-// Example for InstructionType::NoOperand.
-fn test_exit() {
- assert_eq!(asm("exit"), Ok(vec![insn(ebpf::EXIT, 0, 0, 0, 0)]));
-// Example for InstructionType::AluBinary.
-fn test_add64() {
- asm("add64 r1, r3"),
- Ok(vec![insn(ebpf::ADD64_REG, 1, 3, 0, 0)])
- asm("add64 r1, 5"),
- Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, 5)])
-// Example for InstructionType::AluUnary.
-fn test_neg64() {
- assert_eq!(asm("neg64 r1"), Ok(vec![insn(ebpf::NEG64, 1, 0, 0, 0)]));
-// Example for InstructionType::LoadReg.
-fn test_ldxw() {
- asm("ldxw r1, [r2+5]"),
- Ok(vec![insn(ebpf::LD_W_REG, 1, 2, 5, 0)])
-// Example for InstructionType::StoreImm.
-fn test_stw() {
- asm("stw [r2+5], 7"),
- Ok(vec![insn(ebpf::ST_W_IMM, 2, 0, 5, 7)])
-// Example for InstructionType::StoreReg.
-fn test_stxw() {
- asm("stxw [r2+5], r8"),
- Ok(vec![insn(ebpf::ST_W_REG, 2, 8, 5, 0)])
-// Example for InstructionType::JumpUnconditional.
-fn test_ja() {
- assert_eq!(asm("ja +8"), Ok(vec![insn(ebpf::JA, 0, 0, 8, 0)]));
- assert_eq!(asm("ja -3"), Ok(vec![insn(ebpf::JA, 0, 0, -3, 0)]));
-// Example for InstructionType::JumpConditional.
-fn test_jeq() {
- asm("jeq r1, 4, +8"),
- Ok(vec![insn(ebpf::JEQ_IMM, 1, 0, 8, 4)])
- asm("jeq r1, r3, +8"),
- Ok(vec![insn(ebpf::JEQ_REG, 1, 3, 8, 0)])
-// Example for InstructionType::Call.
-fn test_call() {
- assert_eq!(asm("call 300"), Ok(vec![insn(ebpf::CALL, 0, 0, 0, 300)]));
-// Example for InstructionType::Endian.
-fn test_be32() {
- assert_eq!(asm("be32 r1"), Ok(vec![insn(ebpf::BE, 1, 0, 0, 32)]));
-// Example for InstructionType::LoadImm.
-fn test_lddw() {
- asm("lddw r1, 0x1234abcd5678eeff"),
- insn(ebpf::LD_DW_IMM, 1, 0, 0, 0x5678eeff),
- insn(0, 0, 0, 0, 0x1234abcd)
- asm("lddw r1, 0xff11ee22dd33cc44"),
- insn(ebpf::LD_DW_IMM, 1, 0, 0, 0xdd33cc44u32 as i32),
- insn(0, 0, 0, 0, 0xff11ee22u32 as i32)
-// Example for InstructionType::LoadAbs.
-fn test_ldabsw() {
- assert_eq!(asm("ldabsw 1"), Ok(vec![insn(ebpf::LD_ABS_W, 0, 0, 0, 1)]));
-// Example for InstructionType::LoadInd.
-fn test_ldindw() {
- asm("ldindw r1, 2"),
- Ok(vec![insn(ebpf::LD_IND_W, 0, 1, 0, 2)])
-fn test_ldxdw() {
- asm("ldxdw r1, [r2+3]"),
- Ok(vec![insn(ebpf::LD_DW_REG, 1, 2, 3, 0)])
-fn test_sth() {
- asm("sth [r1+2], 3"),
- Ok(vec![insn(ebpf::ST_H_IMM, 1, 0, 2, 3)])
-fn test_stxh() {
- asm("stxh [r1+2], r3"),
- Ok(vec![insn(ebpf::ST_H_REG, 1, 3, 2, 0)])
-// Test all supported AluBinary mnemonics.
-fn test_alu_binary() {
- asm("add r1, r2
- sub r1, r2
- mul r1, r2
- div r1, r2
- or r1, r2
- and r1, r2
- lsh r1, r2
- rsh r1, r2
- mod r1, r2
- xor r1, r2
- mov r1, r2
- arsh r1, r2"),
- insn(ebpf::ADD64_REG, 1, 2, 0, 0),
- insn(ebpf::SUB64_REG, 1, 2, 0, 0),
- insn(ebpf::MUL64_REG, 1, 2, 0, 0),
- insn(ebpf::DIV64_REG, 1, 2, 0, 0),
- insn(ebpf::OR64_REG, 1, 2, 0, 0),
- insn(ebpf::AND64_REG, 1, 2, 0, 0),
- insn(ebpf::LSH64_REG, 1, 2, 0, 0),
- insn(ebpf::RSH64_REG, 1, 2, 0, 0),
- insn(ebpf::MOD64_REG, 1, 2, 0, 0),
- insn(ebpf::XOR64_REG, 1, 2, 0, 0),
- insn(ebpf::MOV64_REG, 1, 2, 0, 0),
- insn(ebpf::ARSH64_REG, 1, 2, 0, 0)
- asm("add r1, 2
- sub r1, 2
- mul r1, 2
- div r1, 2
- or r1, 2
- and r1, 2
- lsh r1, 2
- rsh r1, 2
- mod r1, 2
- xor r1, 2
- mov r1, 2
- arsh r1, 2"),
- insn(ebpf::ADD64_IMM, 1, 0, 0, 2),
- insn(ebpf::SUB64_IMM, 1, 0, 0, 2),
- insn(ebpf::MUL64_IMM, 1, 0, 0, 2),
- insn(ebpf::DIV64_IMM, 1, 0, 0, 2),
- insn(ebpf::OR64_IMM, 1, 0, 0, 2),
- insn(ebpf::AND64_IMM, 1, 0, 0, 2),
- insn(ebpf::LSH64_IMM, 1, 0, 0, 2),
- insn(ebpf::RSH64_IMM, 1, 0, 0, 2),
- insn(ebpf::MOD64_IMM, 1, 0, 0, 2),
- insn(ebpf::XOR64_IMM, 1, 0, 0, 2),
- insn(ebpf::MOV64_IMM, 1, 0, 0, 2),
- insn(ebpf::ARSH64_IMM, 1, 0, 0, 2)
- asm("add64 r1, r2
- sub64 r1, r2
- mul64 r1, r2
- div64 r1, r2
- or64 r1, r2
- and64 r1, r2
- lsh64 r1, r2
- rsh64 r1, r2
- mod64 r1, r2
- xor64 r1, r2
- mov64 r1, r2
- arsh64 r1, r2"),
- asm("add64 r1, 2
- sub64 r1, 2
- mul64 r1, 2
- div64 r1, 2
- or64 r1, 2
- and64 r1, 2
- lsh64 r1, 2
- rsh64 r1, 2
- mod64 r1, 2
- xor64 r1, 2
- mov64 r1, 2
- arsh64 r1, 2"),
- asm("add32 r1, r2
- sub32 r1, r2
- mul32 r1, r2
- div32 r1, r2
- or32 r1, r2
- and32 r1, r2
- lsh32 r1, r2
- rsh32 r1, r2
- mod32 r1, r2
- xor32 r1, r2
- mov32 r1, r2
- arsh32 r1, r2"),
- insn(ebpf::ADD32_REG, 1, 2, 0, 0),
- insn(ebpf::SUB32_REG, 1, 2, 0, 0),
- insn(ebpf::MUL32_REG, 1, 2, 0, 0),
- insn(ebpf::DIV32_REG, 1, 2, 0, 0),
- insn(ebpf::OR32_REG, 1, 2, 0, 0),
- insn(ebpf::AND32_REG, 1, 2, 0, 0),
- insn(ebpf::LSH32_REG, 1, 2, 0, 0),
- insn(ebpf::RSH32_REG, 1, 2, 0, 0),
- insn(ebpf::MOD32_REG, 1, 2, 0, 0),
- insn(ebpf::XOR32_REG, 1, 2, 0, 0),
- insn(ebpf::MOV32_REG, 1, 2, 0, 0),
- insn(ebpf::ARSH32_REG, 1, 2, 0, 0)
- asm("add32 r1, 2
- sub32 r1, 2
- mul32 r1, 2
- div32 r1, 2
- or32 r1, 2
- and32 r1, 2
- lsh32 r1, 2
- rsh32 r1, 2
- mod32 r1, 2
- xor32 r1, 2
- mov32 r1, 2
- arsh32 r1, 2"),
- insn(ebpf::ADD32_IMM, 1, 0, 0, 2),
- insn(ebpf::SUB32_IMM, 1, 0, 0, 2),
- insn(ebpf::MUL32_IMM, 1, 0, 0, 2),
- insn(ebpf::DIV32_IMM, 1, 0, 0, 2),
- insn(ebpf::OR32_IMM, 1, 0, 0, 2),
- insn(ebpf::AND32_IMM, 1, 0, 0, 2),
- insn(ebpf::LSH32_IMM, 1, 0, 0, 2),
- insn(ebpf::RSH32_IMM, 1, 0, 0, 2),
- insn(ebpf::MOD32_IMM, 1, 0, 0, 2),
- insn(ebpf::XOR32_IMM, 1, 0, 0, 2),
- insn(ebpf::MOV32_IMM, 1, 0, 0, 2),
- insn(ebpf::ARSH32_IMM, 1, 0, 0, 2)
-// Test all supported AluUnary mnemonics.
-fn test_alu_unary() {
- asm("neg r1
- neg64 r1
- neg32 r1"),
- insn(ebpf::NEG64, 1, 0, 0, 0),
- insn(ebpf::NEG32, 1, 0, 0, 0)
-// Test all supported LoadAbs mnemonics.
-fn test_load_abs() {
- asm("ldabsw 1
- ldabsh 1
- ldabsb 1
- ldabsdw 1"),
- insn(ebpf::LD_ABS_W, 0, 0, 0, 1),
- insn(ebpf::LD_ABS_H, 0, 0, 0, 1),
- insn(ebpf::LD_ABS_B, 0, 0, 0, 1),
- insn(ebpf::LD_ABS_DW, 0, 0, 0, 1)
-// Test all supported LoadInd mnemonics.
-fn test_load_ind() {
- asm("ldindw r1, 2
- ldindh r1, 2
- ldindb r1, 2
- ldinddw r1, 2"),
- insn(ebpf::LD_IND_W, 0, 1, 0, 2),
- insn(ebpf::LD_IND_H, 0, 1, 0, 2),
- insn(ebpf::LD_IND_B, 0, 1, 0, 2),
- insn(ebpf::LD_IND_DW, 0, 1, 0, 2)
-// Test all supported LoadReg mnemonics.
-fn test_load_reg() {
- asm("ldxw r1, [r2+3]
- ldxh r1, [r2+3]
- ldxb r1, [r2+3]
- ldxdw r1, [r2+3]"),
- insn(ebpf::LD_W_REG, 1, 2, 3, 0),
- insn(ebpf::LD_H_REG, 1, 2, 3, 0),
- insn(ebpf::LD_B_REG, 1, 2, 3, 0),
- insn(ebpf::LD_DW_REG, 1, 2, 3, 0)
-// Test all supported StoreImm mnemonics.
-fn test_store_imm() {
- asm("stw [r1+2], 3
- sth [r1+2], 3
- stb [r1+2], 3
- stdw [r1+2], 3"),
- insn(ebpf::ST_W_IMM, 1, 0, 2, 3),
- insn(ebpf::ST_H_IMM, 1, 0, 2, 3),
- insn(ebpf::ST_B_IMM, 1, 0, 2, 3),
- insn(ebpf::ST_DW_IMM, 1, 0, 2, 3)
-// Test all supported StoreReg mnemonics.
-fn test_store_reg() {
- asm("stxw [r1+2], r3
- stxh [r1+2], r3
- stxb [r1+2], r3
- stxdw [r1+2], r3"),
- insn(ebpf::ST_W_REG, 1, 3, 2, 0),
- insn(ebpf::ST_H_REG, 1, 3, 2, 0),
- insn(ebpf::ST_B_REG, 1, 3, 2, 0),
- insn(ebpf::ST_DW_REG, 1, 3, 2, 0)
-// Test all supported JumpConditional mnemonics.
-fn test_jump_conditional() {
- asm("jeq r1, r2, +3
- jgt r1, r2, +3
- jge r1, r2, +3
- jlt r1, r2, +3
- jle r1, r2, +3
- jset r1, r2, +3
- jne r1, r2, +3
- jsgt r1, r2, +3
- jsge r1, r2, +3
- jslt r1, r2, +3
- jsle r1, r2, +3"),
- insn(ebpf::JEQ_REG, 1, 2, 3, 0),
- insn(ebpf::JGT_REG, 1, 2, 3, 0),
- insn(ebpf::JGE_REG, 1, 2, 3, 0),
- insn(ebpf::JLT_REG, 1, 2, 3, 0),
- insn(ebpf::JLE_REG, 1, 2, 3, 0),
- insn(ebpf::JSET_REG, 1, 2, 3, 0),
- insn(ebpf::JNE_REG, 1, 2, 3, 0),
- insn(ebpf::JSGT_REG, 1, 2, 3, 0),
- insn(ebpf::JSGE_REG, 1, 2, 3, 0),
- insn(ebpf::JSLT_REG, 1, 2, 3, 0),
- insn(ebpf::JSLE_REG, 1, 2, 3, 0)
- asm("jeq r1, 2, +3
- jgt r1, 2, +3
- jge r1, 2, +3
- jlt r1, 2, +3
- jle r1, 2, +3
- jset r1, 2, +3
- jne r1, 2, +3
- jsgt r1, 2, +3
- jsge r1, 2, +3
- jslt r1, 2, +3
- jsle r1, 2, +3"),
- insn(ebpf::JEQ_IMM, 1, 0, 3, 2),
- insn(ebpf::JGT_IMM, 1, 0, 3, 2),
- insn(ebpf::JGE_IMM, 1, 0, 3, 2),
- insn(ebpf::JLT_IMM, 1, 0, 3, 2),
- insn(ebpf::JLE_IMM, 1, 0, 3, 2),
- insn(ebpf::JSET_IMM, 1, 0, 3, 2),
- insn(ebpf::JNE_IMM, 1, 0, 3, 2),
- insn(ebpf::JSGT_IMM, 1, 0, 3, 2),
- insn(ebpf::JSGE_IMM, 1, 0, 3, 2),
- insn(ebpf::JSLT_IMM, 1, 0, 3, 2),
- insn(ebpf::JSLE_IMM, 1, 0, 3, 2)
- asm("jeq32 r1, r2, +3
- jgt32 r1, r2, +3
- jge32 r1, r2, +3
- jlt32 r1, r2, +3
- jle32 r1, r2, +3
- jset32 r1, r2, +3
- jne32 r1, r2, +3
- jsgt32 r1, r2, +3
- jsge32 r1, r2, +3
- jslt32 r1, r2, +3
- jsle32 r1, r2, +3"),
- insn(ebpf::JEQ_REG32, 1, 2, 3, 0),
- insn(ebpf::JGT_REG32, 1, 2, 3, 0),
- insn(ebpf::JGE_REG32, 1, 2, 3, 0),
- insn(ebpf::JLT_REG32, 1, 2, 3, 0),
- insn(ebpf::JLE_REG32, 1, 2, 3, 0),
- insn(ebpf::JSET_REG32, 1, 2, 3, 0),
- insn(ebpf::JNE_REG32, 1, 2, 3, 0),
- insn(ebpf::JSGT_REG32, 1, 2, 3, 0),
- insn(ebpf::JSGE_REG32, 1, 2, 3, 0),
- insn(ebpf::JSLT_REG32, 1, 2, 3, 0),
- insn(ebpf::JSLE_REG32, 1, 2, 3, 0)
- asm("jeq32 r1, 2, +3
- jgt32 r1, 2, +3
- jge32 r1, 2, +3
- jlt32 r1, 2, +3
- jle32 r1, 2, +3
- jset32 r1, 2, +3
- jne32 r1, 2, +3
- jsgt32 r1, 2, +3
- jsge32 r1, 2, +3
- jslt32 r1, 2, +3
- jsle32 r1, 2, +3"),
- insn(ebpf::JEQ_IMM32, 1, 0, 3, 2),
- insn(ebpf::JGT_IMM32, 1, 0, 3, 2),
- insn(ebpf::JGE_IMM32, 1, 0, 3, 2),
- insn(ebpf::JLT_IMM32, 1, 0, 3, 2),
- insn(ebpf::JLE_IMM32, 1, 0, 3, 2),
- insn(ebpf::JSET_IMM32, 1, 0, 3, 2),
- insn(ebpf::JNE_IMM32, 1, 0, 3, 2),
- insn(ebpf::JSGT_IMM32, 1, 0, 3, 2),
- insn(ebpf::JSGE_IMM32, 1, 0, 3, 2),
- insn(ebpf::JSLT_IMM32, 1, 0, 3, 2),
- insn(ebpf::JSLE_IMM32, 1, 0, 3, 2)
-// Test all supported Endian mnemonics.
-fn test_endian() {
- asm("be16 r1
- be32 r1
- be64 r1
- le16 r1
- le32 r1
- le64 r1"),
- insn(ebpf::BE, 1, 0, 0, 16),
- insn(ebpf::BE, 1, 0, 0, 32),
- insn(ebpf::BE, 1, 0, 0, 64),
- insn(ebpf::LE, 1, 0, 0, 16),
- insn(ebpf::LE, 1, 0, 0, 32),
- insn(ebpf::LE, 1, 0, 0, 64)
-fn test_large_immediate() {
- asm("add64 r1, 2147483647"),
- Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, 2147483647)])
- asm("add64 r1, -2147483648"),
- Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, -2147483648)])
-fn test_tcp_sack() {
- assert_eq!(assemble(TCP_SACK_ASM), Ok(TCP_SACK_BIN.to_vec()));
-fn test_error_invalid_instruction() {
- assert_eq!(asm("abcd"), Err("Invalid instruction \"abcd\"".to_string()));
-fn test_error_unexpected_operands() {
- asm("add 1, 2"),
- Err("Failed to encode add: Unexpected operands: [Integer(1), Integer(2)]".to_string())
-fn test_error_too_many_operands() {
- asm("add 1, 2, 3, 4"),
- Err("Failed to encode add: Too many operands".to_string())
-fn test_error_operands_out_of_range() {
- asm("add r16, r2"),
- Err("Failed to encode add: Invalid destination register 16".to_string())
- asm("add r1, r16"),
- Err("Failed to encode add: Invalid source register 16".to_string())
- asm("ja -32769"),
- Err("Failed to encode ja: Invalid offset -32769".to_string())
- asm("ja 32768"),
- Err("Failed to encode ja: Invalid offset 32768".to_string())
- asm("add r1, 4294967296"),
- Err("Failed to encode add: Invalid immediate 4294967296".to_string())
- asm("add r1, 2147483648"),
- Err("Failed to encode add: Invalid immediate 2147483648".to_string())
- asm("add r1, -2147483649"),
- Err("Failed to encode add: Invalid immediate -2147483649".to_string())
@@ -1,97 +0,0 @@
-// Converted from the tests for uBPF <https://github.com/iovisor/ubpf>
-// Assembly code and data for tcp_sack testcases.
-pub const TCP_SACK_ASM: &str = "
- ldxb r2, [r1+12]
- ldxb r3, [r1+13]
- lsh r3, 0x8
- or r3, r2
- mov r0, 0x0
- jne r3, 0x8, +37
- ldxb r2, [r1+23]
- jne r2, 0x6, +35
- ldxb r2, [r1+14]
- add r1, 0xe
- and r2, 0xf
- lsh r2, 0x2
- add r1, r2
- ldxh r4, [r1+12]
- add r1, 0x14
- rsh r4, 0x2
- and r4, 0x3c
- mov r2, r4
- add r2, -20
- mov r5, 0x15
- mov r3, 0x0
- jgt r5, r4, +20
- mov r5, r3
- lsh r5, 0x20
- arsh r5, 0x20
- mov r4, r1
- add r4, r5
- ldxb r5, [r4]
- jeq r5, 0x1, +4
- jeq r5, 0x0, +12
- mov r6, r3
- jeq r5, 0x5, +9
- ja +2
- add r3, 0x1
- ldxb r3, [r4+1]
- add r3, r6
- lsh r3, 0x20
- arsh r3, 0x20
- jsgt r2, r3, -18
- ja +1
- mov r0, 0x1
- exit";
-pub const TCP_SACK_BIN: [u8; 352] = [
- 0x71, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x13, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x25, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x23, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x71, 0x12, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
- 0x57, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x69, 0x14, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x77, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x57, 0x04, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
- 0xbf, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0xec, 0xff, 0xff, 0xff,
- 0xb7, 0x05, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x2d, 0x45, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x67, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xc7, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0xbf, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x71, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x15, 0x05, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x15, 0x05, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x07, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x71, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x67, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xc7, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
- 0x6d, 0x32, 0xee, 0xff, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-pub const TCP_SACK_MATCH: [u8; 78] = [
- 0x00, 0x26, 0x62, 0x2f, 0x47, 0x87, 0x00, 0x1d, 0x60, 0xb3, 0x01, 0x84, 0x08, 0x00, 0x45, 0x00,
- 0x00, 0x40, 0xa8, 0xde, 0x40, 0x00, 0x40, 0x06, 0x9d, 0x58, 0xc0, 0xa8, 0x01, 0x03, 0x3f, 0x74,
- 0xf3, 0x61, 0xe5, 0xc0, 0x00, 0x50, 0xe5, 0x94, 0x3f, 0x77, 0xa3, 0xc4, 0xc4, 0x80, 0xb0, 0x10,
- 0x01, 0x3e, 0x34, 0xb6, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x17, 0x95, 0x6f, 0x8d, 0x9d,
- 0x9e, 0x27, 0x01, 0x01, 0x05, 0x0a, 0xa3, 0xc4, 0xca, 0x28, 0xa3, 0xc4, 0xcf, 0xd0,
-pub const TCP_SACK_NOMATCH: [u8; 66] = [
- 0xf3, 0x61, 0xe5, 0xc0, 0x00, 0x50, 0xe5, 0x94, 0x3f, 0x77, 0xa3, 0xc4, 0xc4, 0x80, 0x80, 0x10,
- 0x9e, 0x27,
@@ -1,2257 +0,0 @@
-#![cfg(feature = "cranelift")]
-use rbpf::{assembler::assemble, helpers};
-use crate::common::{TCP_SACK_ASM, TCP_SACK_MATCH, TCP_SACK_NOMATCH};
-macro_rules! test_cranelift {
- ($name:ident, $prog:expr, $expected:expr) => {
- fn $name() {
- let prog = assemble($prog).unwrap();
- let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
- assert_eq!(vm.execute_program_cranelift().unwrap(), $expected);
- ($name:ident, $prog:expr, $mem:expr, $expected:expr) => {
- let mem = &mut $mem;
- let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap();
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), $expected);
-test_cranelift!(
- test_cranelift_add,
- mov32 r0, 0
- add32 r0, 1
- add32 r0, r1
- exit
- ",
- 0x3
-);
- test_cranelift_alu64_arith,
- mov r0, 0
- mov r1, 1
- mov r2, 2
- mov r3, 3
- mov r4, 4
- mov r5, 5
- mov r6, 6
- mov r7, 7
- mov r8, 8
- mov r9, 9
- add r0, 23
- add r0, r7
- sub r0, 13
- sub r0, r1
- mul r0, 7
- mul r0, r3
- div r0, 2
- div r0, r4
- 0x2a
- test_cranelift_alu64_bit,
- or r0, r5
- or r0, 0xa0
- and r0, 0xa3
- mov r9, 0x91
- and r0, r9
- lsh r0, 32
- lsh r0, 22
- lsh r0, r8
- rsh r0, 32
- rsh r0, 19
- rsh r0, r7
- xor r0, 0x03
- xor r0, r2
- 0x11
- test_cranelift_alu_arith,
- mov32 r1, 1
- mov32 r2, 2
- mov32 r3, 3
- mov32 r4, 4
- mov32 r5, 5
- mov32 r6, 6
- mov32 r7, 7
- mov32 r8, 8
- mov32 r9, 9
- add32 r0, 23
- add32 r0, r7
- sub32 r0, 13
- sub32 r0, r1
- mul32 r0, 7
- mul32 r0, r3
- div32 r0, 2
- div32 r0, r4
- test_cranelift_alu_bit,
- or32 r0, r5
- or32 r0, 0xa0
- and32 r0, 0xa3
- mov32 r9, 0x91
- and32 r0, r9
- lsh32 r0, 22
- lsh32 r0, r8
- rsh32 r0, 19
- rsh32 r0, r7
- xor32 r0, 0x03
- xor32 r0, r2
- test_cranelift_arsh32_high_shift,
- mov r0, 8
- lddw r1, 0x100000001
- arsh32 r0, r1
- 0x4
- test_cranelift_arsh,
- mov32 r0, 0xf8
- lsh32 r0, 28
- arsh32 r0, 16
- 0xffff8000
- test_cranelift_arsh64,
- mov32 r0, 1
- lsh r0, 63
- arsh r0, 55
- mov32 r1, 5
- arsh r0, r1
- 0xfffffffffffffff8
- test_cranelift_arsh_reg,
- mov32 r1, 16
- test_cranelift_be16,
- ldxh r0, [r1]
- [0x11, 0x22],
- 0x1122
- test_cranelift_be16_high,
- ldxdw r0, [r1]
- [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
- test_cranelift_be32,
- ldxw r0, [r1]
- be32 r0
- [0x11, 0x22, 0x33, 0x44],
- 0x11223344
- test_cranelift_be32_high,
- test_cranelift_be64,
- be64 r0
- 0x1122334455667788
-fn test_cranelift_call() {
- let prog = assemble(
- call 0
- exit",
- vm.register_helper(0, helpers::gather_bytes).unwrap();
- assert_eq!(vm.execute_program_cranelift().unwrap(), 0x0102030405);
-#[should_panic(expected = "[CRANELIFT] Error: unknown helper function (id: 0x3f)")]
-fn test_cranelift_err_call_unreg() {
- call 63
-fn test_cranelift_call_memfrob() {
- mov r6, r1
- add r1, 2
- mov r2, 4
- call 1
- ldxdw r0, [r6]
- vm.register_helper(1, helpers::memfrob).unwrap();
- let mem = &mut [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
- vm.execute_program_cranelift(mem).unwrap(),
- 0x102292e2f2c0708
- test_cranelift_div32_high_divisor,
- mov r0, 12
- lddw r1, 0x100000004
- div32 r0, r1
- test_cranelift_div32_imm,
- lddw r0, 0x10000000c
- div32 r0, 4
- test_cranelift_div32_reg,
- mov r1, 4
- test_cranelift_div64_imm,
- mov r0, 0xc
- div r0, 4
- 0x300000000
- test_cranelift_div64_reg,
- div r0, r1
- test_cranelift_early_exit,
- mov r0, 3
- mov r0, 4
- test_cranelift_div64_by_zero_imm,
- div r0, 0
- 0x0
- test_cranelift_div_by_zero_imm,
- div32 r0, 0
- test_cranelift_mod64_by_zero_imm,
- mod r0, 0
- 0x1
- test_cranelift_mod_by_zero_imm,
- mod32 r0, 0
- test_cranelift_div64_by_zero_reg,
- mov32 r1, 0
- test_cranelift_div_by_zero_reg,
- test_cranelift_mod64_by_zero_reg,
- mod r0, r1
- test_cranelift_mod_by_zero_reg,
- mod32 r0, r1
-// #[should_panic(expected = "Error: out of bounds memory store (insn #1)")]
-#[ignore = "We have stack OOB checks, but we don't yet catch the trap code and convert it into a panic"]
-fn test_cranelift_err_stack_out_of_bound() {
- let prog = [
- 0x72, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00,
- vm.execute_program_cranelift().unwrap();
- test_cranelift_exit,
- test_cranelift_ja,
- mov r0, 1
- mov r0, 2
- test_cranelift_jeq_imm,
- mov32 r1, 0xa
- jeq r1, 0xb, +4
- mov32 r1, 0xb
- jeq r1, 0xb, +1
- mov32 r0, 2
- test_cranelift_jeq_reg,
- mov32 r2, 0xb
- jeq r1, r2, +4
- jeq r1, r2, +1
- test_cranelift_jge_imm,
- jge r1, 0xb, +4
- mov32 r1, 0xc
- jge r1, 0xb, +1
- test_cranelift_jle_imm,
- jle r1, 4, +1
- jle r1, 6, +1
- jle r1, 5, +1
- test_cranelift_jle_reg,
- mov r1, 5
- mov r3, 6
- jle r1, r2, +2
- jle r1, r1, +1
- jle r1, r3, +1
- test_cranelift_jgt_imm,
- jgt r1, 6, +2
- jgt r1, 5, +1
- jgt r1, 4, +1
- test_cranelift_jgt_reg,
- mov r2, 6
- mov r3, 4
- jgt r1, r2, +2
- jgt r1, r1, +1
- jgt r1, r3, +1
- test_cranelift_jlt_imm,
- jlt r1, 4, +2
- jlt r1, 5, +1
- jlt r1, 6, +1
- test_cranelift_jlt_reg,
- jlt r1, r2, +2
- jlt r1, r1, +1
- jlt r1, r3, +1
- test_cranelift_jit_bounce,
- mov r6, r0
- mov r7, r6
- mov r8, r7
- mov r9, r8
- mov r0, r9
- test_cranelift_jne_reg,
- jne r1, r2, +4
- jne r1, r2, +1
- test_cranelift_jset_imm,
- mov32 r1, 0x7
- jset r1, 0x8, +4
- mov32 r1, 0x9
- jset r1, 0x8, +1
- test_cranelift_jset_reg,
- mov32 r2, 0x8
- jset r1, r2, +4
- jset r1, r2, +1
- test_cranelift_jsge_imm,
- mov r1, -2
- jsge r1, -1, +5
- jsge r1, 0, +4
- mov r1, -1
- jsge r1, -1, +1
- test_cranelift_jsge_reg,
- mov r2, -1
- mov32 r3, 0
- jsge r1, r2, +5
- jsge r1, r3, +4
- jsge r1, r2, +1
- test_cranelift_jsle_imm,
- jsle r1, -3, +1
- jsle r1, -1, +1
- jsle r1, -2, +1
- test_cranelift_jsle_reg,
- mov r2, -2
- jsle r1, r2, +1
- jsle r1, r3, +1
- test_cranelift_jsgt_imm,
- jsgt r1, -1, +4
- jsgt r1, -1, +1
- test_cranelift_jsgt_reg,
- jsgt r1, r2, +4
- jsgt r1, r2, +1
- test_cranelift_jslt_imm,
- jslt r1, -3, +2
- jslt r1, -2, +1
- jslt r1, -1, +1
- test_cranelift_jslt_reg,
- mov r2, -3
- mov r3, -1
- jslt r1, r1, +2
- jslt r1, r2, +1
- jslt r1, r3, +1
- test_cranelift_jeq32_imm,
- mov r9, 1
- lsh r9, 32
- mov32 r0, 0x0
- jeq32 r1, 0xb, +5
- mov r1, 0xb
- or r1, r9
- jeq32 r1, 0xb, +1
- test_cranelift_jeq32_reg,
- jeq32 r1, r2, +5
- jeq32 r1, r2, +1
- test_cranelift_jge32_imm,
- jge32 r1, 0xb, +5
- jge32 r1, 0xb, +1
- test_cranelift_jge32_reg,
- jge32 r1, r2, +5
- jge32 r1, r2, +1
- test_cranelift_jgt32_imm,
- jgt32 r1, 6, +4
- jgt32 r1, 5, +3
- jgt32 r1, 4, +1
- test_cranelift_jgt32_reg,
- jgt32 r1, r2, +4
- jgt32 r1, r1, +3
- jgt32 r1, r3, +1
- test_cranelift_jle32_imm,
- jle32 r1, 4, +5
- jle32 r1, 6, +1
- jle32 r1, 5, +1
- test_cranelift_jle32_reg,
- jle32 r1, r2, +5
- jle32 r1, r1, +1
- jle32 r1, r3, +1
- test_cranelift_jlt32_imm,
- jlt32 r1, 4, +4
- jlt32 r1, 5, +3
- jlt32 r1, 6, +1
- test_cranelift_jlt32_reg,
- jlt32 r1, r2, +4
- jlt32 r1, r1, +3
- jlt32 r1, r3, +1
- test_cranelift_jne32_imm,
- jne32 r1, 0xb, +4
- jne32 r1, 0xb, +1
- test_cranelift_jne32_reg,
- jne32 r1, r2, +4
- jne32 r1, r2, +1
- test_cranelift_jset32_imm,
- jset32 r1, 0x8, +4
- jset32 r1, 0x8, +1
- test_cranelift_jset32_reg,
- jset32 r1, r2, +4
- jset32 r1, r2, +1
- test_cranelift_jsge32_imm,
- mov32 r1, -2
- jsge32 r1, -1, +5
- jsge32 r1, 0, +4
- jsge32 r1, -1, +1
- test_cranelift_jsge32_reg,
- jsge32 r1, r2, +5
- jsge32 r1, r3, +4
- jsge32 r1, r2, +1
- test_cranelift_jsgt32_imm,
- jsgt32 r1, -1, +4
- jsgt32 r1, -1, +1
- test_cranelift_jsgt32_reg,
- jsgt32 r1, r2, +4
- jsgt32 r1, r2, +1
- test_cranelift_jsle32_imm,
- jsle32 r1, -3, +5
- jsle32 r1, -1, +1
- jsle32 r1, -2, +1
- test_cranelift_jsle32_reg,
- jsle32 r1, r2, +6
- jsle32 r1, r3, +1
- jsle32 r1, r2, +1
- test_cranelift_jslt32_imm,
- jslt32 r1, -3, +4
- jslt32 r1, -2, +3
- jslt32 r1, -1, +1
- test_cranelift_jslt32_reg,
- jslt32 r1, r1, +4
- jslt32 r1, r3, +1
- test_cranelift_lddw,
- lddw r0, 0x1122334455667788
- test_cranelift_lddw2,
- lddw r0, 0x0000000080000000
- 0x80000000
- test_cranelift_ldxb_all,
- mov r0, r1
- ldxb r9, [r0+0]
- lsh r9, 0
- ldxb r8, [r0+1]
- lsh r8, 4
- ldxb r7, [r0+2]
- lsh r7, 8
- ldxb r6, [r0+3]
- lsh r6, 12
- ldxb r5, [r0+4]
- lsh r5, 16
- ldxb r4, [r0+5]
- lsh r4, 20
- ldxb r3, [r0+6]
- lsh r3, 24
- ldxb r2, [r0+7]
- lsh r2, 28
- ldxb r1, [r0+8]
- lsh r1, 32
- ldxb r0, [r0+9]
- lsh r0, 36
- or r0, r1
- or r0, r2
- or r0, r3
- or r0, r4
- or r0, r6
- or r0, r7
- or r0, r8
- or r0, r9
- [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09],
- 0x9876543210
- test_cranelift_ldxb,
- ldxb r0, [r1+2]
- [0xaa, 0xbb, 0x11, 0xcc, 0xdd],
- test_cranelift_ldxdw,
- ldxdw r0, [r1+2]
- [0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xcc, 0xdd],
- 0x8877665544332211
- test_cranelift_ldxh_all,
- ldxh r9, [r0+0]
- be16 r9
- ldxh r8, [r0+2]
- be16 r8
- ldxh r7, [r0+4]
- be16 r7
- ldxh r6, [r0+6]
- be16 r6
- ldxh r5, [r0+8]
- be16 r5
- ldxh r4, [r0+10]
- be16 r4
- ldxh r3, [r0+12]
- be16 r3
- ldxh r2, [r0+14]
- be16 r2
- ldxh r1, [r0+16]
- be16 r1
- ldxh r0, [r0+18]
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00,
- 0x07, 0x00, 0x08, 0x00, 0x09
- test_cranelift_ldxh_all2,
- 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00,
- 0x80, 0x01, 0x00, 0x02, 0x00
- 0x3ff
- test_cranelift_ldxh,
- ldxh r0, [r1+2]
- [0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd],
- 0x2211
- test_cranelift_ldxh_same_reg,
- sth [r0], 0x1234
- ldxh r0, [r0]
- [0xff, 0xff],
- 0x1234
- test_cranelift_ldxw_all,
- ldxw r9, [r0+0]
- be32 r9
- ldxw r8, [r0+4]
- be32 r8
- ldxw r7, [r0+8]
- be32 r7
- ldxw r6, [r0+12]
- be32 r6
- ldxw r5, [r0+16]
- be32 r5
- ldxw r4, [r0+20]
- be32 r4
- ldxw r3, [r0+24]
- be32 r3
- ldxw r2, [r0+28]
- be32 r2
- ldxw r1, [r0+32]
- ldxw r0, [r0+36]
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00
- 0x030f0f
- test_cranelift_ldxw,
- ldxw r0, [r1+2]
- [0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0xcc, 0xdd],
- 0x44332211
- test_cranelift_le16,
- le16 r0
- [0x22, 0x11],
- test_cranelift_le32,
- le32 r0
- [0x44, 0x33, 0x22, 0x11],
- test_cranelift_le64,
- le64 r0
- [0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11],
- test_cranelift_lsh_reg,
- mov r7, 4
- lsh r0, r7
- 0x10
- test_cranelift_mod,
- mov32 r0, 5748
- mod32 r0, 92
- mov32 r1, 13
- 0x5
- test_cranelift_mod32,
- lddw r0, 0x100000003
- mod32 r0, 3
- test_cranelift_mod64,
- mov32 r0, -1316649930
- or r0, 0x100dc5c8
- mov32 r1, 0xdde263e
- or r1, 0x3cbef7f3
- mod r0, 0x658f1778
- 0x30ba5a04
- test_cranelift_mov,
- mov32 r0, r1
- test_cranelift_mul32_imm,
- mul32 r0, 4
- 0xc
- test_cranelift_mul32_reg,
- mul32 r0, r1
- test_cranelift_mul32_reg_overflow,
- mov r0, 0x40000001
- test_cranelift_mul64_imm,
- mul r0, 4
- 0x100000004
- test_cranelift_mul64_reg,
- mul r0, r1
- test_cranelift_mul_loop,
- mov r0, 0x7
- add r1, 0xa
- lsh r1, 0x20
- rsh r1, 0x20
- jeq r1, 0x0, +4
- mul r0, 0x7
- add r1, -1
- jne r1, 0x0, -3
- 0x75db9c97
- test_cranelift_neg64,
- neg r0
- 0xfffffffffffffffe
- test_cranelift_neg,
- neg32 r0
- 0xfffffffe
- test_cranelift_prime,
- mov r1, 67
- mov r2, 0x2
- jgt r1, 0x2, +4
- ja +10
- add r2, 0x1
- jge r2, r1, +7
- mov r3, r1
- div r3, r2
- mul r3, r2
- sub r4, r3
- jne r4, 0x0, -10
- test_cranelift_rhs32,
- xor r0, r0
- sub r0, 1
- rsh32 r0, 8
- 0x00ffffff
- test_cranelift_rsh_reg,
- mov r0, 0x10
- test_cranelift_stack,
- mov r1, 51
- stdw [r10-16], 0xab
- stdw [r10-8], 0xcd
- and r1, 1
- lsh r1, 3
- mov r2, r10
- add r2, r1
- ldxdw r0, [r2-16]
- 0xcd
-fn test_cranelift_stack2() {
- stb [r10-4], 0x01
- stb [r10-3], 0x02
- stb [r10-2], 0x03
- stb [r10-1], 0x04
- mov r1, r10
- mov r2, 0x4
- mov r1, 0
- ldxb r2, [r10-4]
- ldxb r3, [r10-3]
- ldxb r4, [r10-2]
- ldxb r5, [r10-1]
- xor r0, 0x2a2a2a2a
- assert_eq!(vm.execute_program_cranelift().unwrap(), 0x01020304);
- test_cranelift_stb,
- stb [r1+2], 0x11
- [0xaa, 0xbb, 0xff, 0xcc, 0xdd],
- test_cranelift_stdw,
- stdw [r1+2], 0x44332211
- [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd],
- test_cranelift_sth,
- sth [r1+2], 0x2211
- [0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd],
-#[ignore]
-fn test_cranelift_string_stack() {
- mov r1, 0x78636261
- stxw [r10-8], r1
- mov r6, 0x0
- stxb [r10-4], r6
- stxb [r10-12], r6
- mov r1, 0x79636261
- stxw [r10-16], r1
- add r1, -8
- mov r2, r1
- call 0x4
- mov r1, r0
- jne r1, 0x0, +11
- add r2, -16
- jeq r1, r6, +1
- vm.register_helper(4, helpers::strcmp).unwrap();
- assert_eq!(vm.execute_program_cranelift().unwrap(), 0x0);
- test_cranelift_stw,
- stw [r1+2], 0x44332211
- [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd],
- test_cranelift_stxb,
- mov32 r2, 0x11
- stxb [r1+2], r2
- test_cranelift_stxb_all,
- mov r0, 0xf0
- mov r2, 0xf2
- mov r3, 0xf3
- mov r4, 0xf4
- mov r5, 0xf5
- mov r6, 0xf6
- mov r7, 0xf7
- mov r8, 0xf8
- stxb [r1], r0
- stxb [r1+1], r2
- stxb [r1+3], r4
- stxb [r1+4], r5
- stxb [r1+5], r6
- stxb [r1+6], r7
- stxb [r1+7], r8
- [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
- 0xf0f2f3f4f5f6f7f8
- test_cranelift_stxb_all2,
- mov r1, 0xf1
- mov r9, 0xf9
- stxb [r0], r1
- stxb [r0+1], r9
- 0xf1f9
- test_cranelift_stxb_chain,
- stxb [r0+2], r8
- stxb [r0+3], r7
- stxb [r0+4], r6
- stxb [r0+5], r5
- stxb [r0+6], r4
- stxb [r0+7], r3
- stxb [r0+8], r2
- stxb [r0+9], r1
- [0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
- test_cranelift_stxdw,
- mov r2, -2005440939
- lsh r2, 32
- or r2, 0x44332211
- stxdw [r1+2], r2
- test_cranelift_stxh,
- mov32 r2, 0x2211
- stxh [r1+2], r2
- test_cranelift_stxw,
- mov32 r2, 0x44332211
- stxw [r1+2], r2
- test_cranelift_subnet,
- mov r2, 0xe
- ldxh r3, [r1+12]
- jne r3, 0x81, +2
- mov r2, 0x12
- ldxh r3, [r1+16]
- and r3, 0xffff
- jne r3, 0x8, +5
- ldxw r1, [r1+16]
- and r1, 0xffffff
- jeq r1, 0x1a8c0, +1
- 0x00, 0x00, 0xc0, 0x9f, 0xa0, 0x97, 0x00, 0xa0, 0xcc, 0x3b, 0xbf, 0xfa, 0x08, 0x00, 0x45,
- 0x10, 0x00, 0x3c, 0x46, 0x3c, 0x40, 0x00, 0x40, 0x06, 0x73, 0x1c, 0xc0, 0xa8, 0x01, 0x02,
- 0xc0, 0xa8, 0x01, 0x01, 0x06, 0x0e, 0x00, 0x17, 0x99, 0xc5, 0xa0, 0xec, 0x00, 0x00, 0x00,
- 0x00, 0xa0, 0x02, 0x7d, 0x78, 0xe0, 0xa3, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02,
- 0x08, 0x0a, 0x00, 0x9c, 0x27, 0x24, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x00,
-const PROG_TCP_PORT_80: [u8; 152] = [
- 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x0a, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x15, 0x02, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x69, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x55, 0x01, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-fn test_cranelift_tcp_port80_match() {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45,
- 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01,
- 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
- 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
- 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
- let prog = &PROG_TCP_PORT_80;
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x1);
-fn test_cranelift_tcp_port80_nomatch() {
- 0xc0, 0xa8, 0x00, 0x02, 0x00, 0x16, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x51, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x0);
-fn test_cranelift_tcp_port80_nomatch_ethertype() {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x01, 0x45,
-fn test_cranelift_tcp_port80_nomatch_proto() {
- 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01,
-fn test_cranelift_tcp_sack_match() {
- let mut mem = TCP_SACK_MATCH.to_vec();
- let prog = assemble(TCP_SACK_ASM).unwrap();
- vm.execute_program_cranelift(mem.as_mut_slice()).unwrap(),
-fn test_cranelift_tcp_sack_nomatch() {
- let mut mem = TCP_SACK_NOMATCH.to_vec();
-fn test_cranelift_ldabsb() {
- 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
- 0xff,
- let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap();
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x33);
-fn test_cranelift_ldabsh() {
- 0x28, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x4433);
-fn test_cranelift_ldabsw() {
- 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x66554433);
-fn test_cranelift_ldabsdw() {
- 0x38, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xaa99887766554433
-fn test_cranelift_ldindb() {
- 0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x50, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x88);
-fn test_cranelift_ldindh() {
- 0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00,
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x9988);
-fn test_cranelift_ldindw() {
- 0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00,
- assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x88776655);
-fn test_cranelift_ldinddw() {
- 0xb7, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00,
- 0xccbbaa9988776655
@@ -1,377 +0,0 @@
-// Copyright 2017 Jan-Erik Rediger <badboy@archlinux.us>
-// Adopted from tests in `tests/assembler.rs`
-use rbpf::{assembler::assemble, disassembler::to_insn_vec};
-// Using a macro to keep actual line numbers in failure output
-macro_rules! disasm {
- ($src:expr) => {{
- let src = $src;
- let asm = assemble(src).expect("Can't assemble from string");
- let insn = to_insn_vec(&asm);
- let reasm = insn
- .into_iter()
- .map(|ins| ins.desc)
- .join("\n");
- assert_eq!(src, reasm);
- disasm!("");
- disasm!("exit");
- disasm!("add64 r1, r3");
- disasm!("add64 r1, 0x5");
- disasm!("neg64 r1");
- disasm!("ldxw r1, [r2+0x5]");
- disasm!("stw [r2+0x5], 0x7");
- disasm!("stxw [r2+0x5], r8");
- disasm!("ja +0x8");
- disasm!("jeq r1, 0x4, +0x8");
- disasm!("jeq r1, r3, +0x8");
- disasm!("call 0x3");
- disasm!("be32 r1");
- disasm!("lddw r1, 0x1234abcd5678eeff");
- disasm!("lddw r1, 0xff11ee22dd33cc44");
- disasm!("ldabsw 0x1");
- disasm!("ldindw r1, 0x2");
- disasm!("ldxdw r1, [r2+0x3]");
- disasm!("sth [r1+0x2], 0x3");
- disasm!("stxh [r1+0x2], r3");
- disasm!(
- "add64 r1, r2
-sub64 r1, r2
-mul64 r1, r2
-div64 r1, r2
-or64 r1, r2
-and64 r1, r2
-lsh64 r1, r2
-rsh64 r1, r2
-mod64 r1, r2
-xor64 r1, r2
-mov64 r1, r2
-arsh64 r1, r2"
- "add64 r1, 0x2
-sub64 r1, 0x2
-mul64 r1, 0x2
-div64 r1, 0x2
-or64 r1, 0x2
-and64 r1, 0x2
-lsh64 r1, 0x2
-rsh64 r1, 0x2
-mod64 r1, 0x2
-xor64 r1, 0x2
-mov64 r1, 0x2
-arsh64 r1, 0x2"
- "add32 r1, r2
-sub32 r1, r2
-mul32 r1, r2
-div32 r1, r2
-or32 r1, r2
-and32 r1, r2
-lsh32 r1, r2
-rsh32 r1, r2
-mod32 r1, r2
-xor32 r1, r2
-mov32 r1, r2
-arsh32 r1, r2"
- "add32 r1, 0x2
-sub32 r1, 0x2
-mul32 r1, 0x2
-div32 r1, 0x2
-or32 r1, 0x2
-and32 r1, 0x2
-lsh32 r1, 0x2
-rsh32 r1, 0x2
-mod32 r1, 0x2
-xor32 r1, 0x2
-mov32 r1, 0x2
-arsh32 r1, 0x2"
- "neg64 r1
-neg32 r1"
- "ldabsw 0x1
-ldabsh 0x1
-ldabsb 0x1
-ldabsdw 0x1"
- "ldindw r1, 0x2
-ldindh r1, 0x2
-ldindb r1, 0x2
-ldinddw r1, 0x2"
- r"ldxw r1, [r2+0x3]
-ldxh r1, [r2+0x3]
-ldxb r1, [r2+0x3]
-ldxdw r1, [r2+0x3]"
- "stw [r1+0x2], 0x3
-sth [r1+0x2], 0x3
-stb [r1+0x2], 0x3
-stdw [r1+0x2], 0x3"
- "stxw [r1+0x2], r3
-stxh [r1+0x2], r3
-stxb [r1+0x2], r3
-stxdw [r1+0x2], r3"
- "jeq r1, r2, +0x3
-jgt r1, r2, +0x3
-jge r1, r2, +0x3
-jlt r1, r2, +0x3
-jle r1, r2, +0x3
-jset r1, r2, +0x3
-jne r1, r2, +0x3
-jsgt r1, r2, +0x3
-jsge r1, r2, -0x3
-jslt r1, r2, +0x3
-jsle r1, r2, -0x3"
- "jeq r1, 0x2, +0x3
-jgt r1, 0x2, +0x3
-jge r1, 0x2, +0x3
-jlt r1, 0x2, +0x3
-jle r1, 0x2, +0x3
-jset r1, 0x2, +0x3
-jne r1, 0x2, +0x3
-jsgt r1, 0x2, +0x3
-jsge r1, 0x2, -0x3
-jslt r1, 0x2, +0x3
-jsle r1, 0x2, -0x3"
- "jeq32 r1, r2, +0x3
-jgt32 r1, r2, +0x3
-jge32 r1, r2, +0x3
-jlt32 r1, r2, +0x3
-jle32 r1, r2, +0x3
-jset32 r1, r2, +0x3
-jne32 r1, r2, +0x3
-jsgt32 r1, r2, +0x3
-jsge32 r1, r2, -0x3
-jslt32 r1, r2, +0x3
-jsle32 r1, r2, -0x3"
- "jeq32 r1, 0x2, +0x3
-jgt32 r1, 0x2, +0x3
-jge32 r1, 0x2, +0x3
-jlt32 r1, 0x2, +0x3
-jle32 r1, 0x2, +0x3
-jset32 r1, 0x2, +0x3
-jne32 r1, 0x2, +0x3
-jsgt32 r1, 0x2, +0x3
-jsge32 r1, 0x2, -0x3
-jslt32 r1, 0x2, +0x3
-jsle32 r1, 0x2, -0x3"
- "be16 r1
-be32 r1
-be64 r1
-le16 r1
-le32 r1
-le64 r1"
- disasm!("add64 r1, 0x7fffffff");
-// Non-regression tests for overflow when trying to negate offset 0x8000i16.
-fn test_offset_overflow() {
- let insns = [
- 0x62, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stw
- 0x6a, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // sth
- 0x72, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stb
- 0x7a, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stdw
- 0x61, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxw
- 0x69, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxh
- 0x71, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxb
- 0x79, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxdw
- 0x15, 0x01, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, // jeq (imm)
- 0x1d, 0x21, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // jeq (reg)
- 0x16, 0x01, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, // jeq32 (imm)
- 0x1e, 0x21, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // jeq32 (reg)
- let expected_output = "stw [r1-0x8000], 0x1
-sth [r1-0x8000], 0x1
-stb [r1-0x8000], 0x1
-stdw [r1-0x8000], 0x1
-ldxw r1, [r0-0x8000]
-ldxh r1, [r0-0x8000]
-ldxb r1, [r0-0x8000]
-ldxdw r1, [r0-0x8000]
-jeq r1, 0x2, -0x8000
-jeq r1, r2, -0x8000
-jeq32 r1, 0x2, -0x8000
-jeq32 r1, r2, -0x8000";
- let prog = to_insn_vec(&insns);
- let asm = prog
- assert_eq!(asm, expected_output);
@@ -1,571 +0,0 @@
-// This crate would be needed to load bytecode from a BPF-compiled object file. Since the crate
-// is not used anywhere else in the library, it is deactivated: we do not want to load and compile
-// it just for the tests. If you want to use it, do not forget to add the following
-// dependency to your Cargo.toml file:
-// ---
-// elf = "0.0.10"
-// extern crate elf;
-// use std::path::PathBuf;
-use rbpf::{assembler::assemble, Error, ErrorKind};
-// The following two examples have been compiled from C with the following command:
-// clang -O2 -emit-llvm -c <file.c> -o - | llc -march=bpf -filetype=obj -o <file.o>
-// The C source code was the following:
-// ```c
-// #include <linux/ip.h>
-// #include <linux/in.h>
-// #include <linux/tcp.h>
-// #include <linux/bpf.h>
-// #define ETH_ALEN 6
-// #define ETH_P_IP 0x0008 /* htons(0x0800) */
-// #define TCP_HDR_LEN 20
-// #define BLOCKED_TCP_PORT 0x9999
-// struct eth_hdr {
-// unsigned char h_dest[ETH_ALEN];
-// unsigned char h_source[ETH_ALEN];
-// unsigned short h_proto;
-// };
-// #define SEC(NAME) __attribute__((section(NAME), used))
-// SEC(".classifier")
-// int handle_ingress(struct __sk_buff *skb)
-// {
-// void *data = (void *)(long)skb->data;
-// void *data_end = (void *)(long)skb->data_end;
-// struct eth_hdr *eth = data;
-// struct iphdr *iph = data + sizeof(*eth);
-// struct tcphdr *tcp = data + sizeof(*eth) + sizeof(*iph);
-// /* single length check */
-// if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcp) > data_end)
-// return 0;
-// if (eth->h_proto != ETH_P_IP)
-// if (iph->protocol != IPPROTO_TCP)
-// if (tcp->source == BLOCKED_TCP_PORT || tcp->dest == BLOCKED_TCP_PORT)
-// return -1;
-// char _license[] SEC(".license") = "GPL";
-// This program, once compiled, can be injected into Linux kernel, with tc for instance. Sadly, we
-// for example, 0x40 and 0x50. See comments on the bytecode below to see the modifications.
-// Once the bytecode has been (manually, in our case) edited, we can load the bytecode directly
-// from the ELF object file. This is easy to do, but requires the addition of two crates in the
-// Cargo.toml file (see comments above), so here we use just the hardcoded bytecode instructions
-// instead.
-fn test_vm_block_port() {
- // To load the bytecode from an object file instead of using the hardcoded instructions,
- // use the additional crates commented at the beginning of this file (and also add them to your
- // Cargo.toml). See comments above.
- // ---
- // let filename = "my_ebpf_object_file.o";
- // let path = PathBuf::from(filename);
- // let file = match elf::File::open_path(&path) {
- // Ok(f) => f,
- // Err(e) => panic!("Error: {:?}", e),
- // };
- // let text_scn = match file.get_section(".classifier") {
- // Some(s) => s,
- // None => panic!("Failed to look up .classifier section"),
- // let prog = &text_scn.data;
- 0x00, // 0x79 instead of 0x61
- 0x79, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00,
- 0x00, // 0x79 instead of 0x61, 0x40 i.o. 0x4c
- 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00,
- 0x00, 0x2d, 0x23, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x12, 0x0c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x55, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x71, 0x12, 0x17, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x55, 0x02, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x11, 0x22,
- 0x00, 0x00, 0x00, 0x00, 0x00, // 0x79 instead of 0x61
- 0xbf, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00,
- 0x00, 0x15, 0x02, 0x08, 0x00, 0x99, 0x99, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x21, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x18, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x21, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00,
- println!("Program returned: {res:?} ({res:#x})");
-fn test_jit_block_port() {
- let res = vm.execute_program_jit(packet).unwrap();
-// Program and memory come from uBPF test ldxh.
-fn test_vm_mbuff() {
- // Load mem from mbuff into R1
- 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0
- 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- let mem = &[0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd];
- let mbuff = [0u8; 32];
- data.write_unaligned(mem.as_ptr() as u64);
- data_end.write_unaligned(mem.as_ptr() as u64 + mem.len() as u64);
- let vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap();
- assert_eq!(vm.execute_program(mem, &mbuff).unwrap(), 0x2211);
-fn test_vm_mbuff_with_rust_api() {
- use rbpf::insn_builder::*;
- .set_off(0x00_08)
- let vm = rbpf::EbpfVmMbuff::new(Some(program.into_bytes())).unwrap();
-fn test_jit_mbuff() {
- let mem = &mut [0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd];
- let mut mbuff = [0u8; 32];
- assert_eq!(vm.execute_program_jit(mem, &mut mbuff).unwrap(), 0x2211);
-fn test_vm_jit_ldabsb() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x33);
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x33);
-fn test_vm_jit_ldabsh() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x4433);
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x4433);
-fn test_vm_jit_ldabsw() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x66554433);
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x66554433);
-fn test_vm_jit_ldabsdw() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0xaa99887766554433);
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0xaa99887766554433);
-#[should_panic(expected = "Error: out of bounds memory load (insn #1),")]
-fn test_vm_err_ldabsb_oob() {
- 0x38, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap();
- vm.execute_program(mem).unwrap();
- // Memory check not implemented for JIT yet.
-fn test_vm_err_ldabsb_nomem() {
- vm.execute_program().unwrap();
-fn test_vm_jit_ldindb() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x88);
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x88);
-fn test_vm_jit_ldindh() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x9988);
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x9988);
-fn test_vm_jit_ldindw() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x88776655);
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x88776655);
-fn test_vm_jit_ldinddw() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0xccbbaa9988776655);
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0xccbbaa9988776655);
-#[should_panic(expected = "Error: out of bounds memory load (insn #2),")]
-fn test_vm_err_ldindb_oob() {
- 0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x38, 0x10, 0x00, 0x00, 0x33, 0x00, 0x00,
-fn test_vm_err_ldindb_nomem() {
- 0xb7, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x38, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00,
-#[should_panic(expected = "Error: No program set, call prog_set() to load one")]
-fn test_vm_exec_no_program() {
- let vm = rbpf::EbpfVmNoData::new(None).unwrap();
- assert_eq!(vm.execute_program().unwrap(), 0xBEE);
-fn verifier_success(_prog: &[u8]) -> Result<(), Error> {
-fn verifier_fail(_prog: &[u8]) -> Result<(), Error> {
- Err(Error::new(ErrorKind::Other, "Gaggablaghblagh!"))
-fn test_verifier_success() {
- "mov32 r0, 0xBEE
- let mut vm = rbpf::EbpfVmNoData::new(None).unwrap();
- vm.set_verifier(verifier_success).unwrap();
- vm.set_program(&prog).unwrap();
-#[should_panic(expected = "Gaggablaghblagh!")]
-fn test_verifier_fail() {
- vm.set_verifier(verifier_fail).unwrap();
@@ -1,2891 +0,0 @@
-// The tests contained in this file are extracted from the unit tests of uBPF software. Each test
-// in this file has a name in the form `test_jit_<name>`, and corresponds to the (human-readable)
-// code in `ubpf/tree/master/tests/<name>`, available at
-// <https://github.com/iovisor/ubpf/tree/master/tests> (hyphen had to be replaced with underscores
-// as Rust will not accept them in function names). It is strongly advised to refer to the uBPF
-// version to understand what these program do.
-// Each program was assembled from the uBPF version with the assembler provided by uBPF itself, and
-// available at <https://github.com/iovisor/ubpf/tree/master/ubpf>.
-// The very few modifications that have been realized should be indicated.
-// These are unit tests for the eBPF JIT compiler.
-#![cfg(all(not(windows), feature = "std"))]
-use common::{TCP_SACK_ASM, TCP_SACK_MATCH, TCP_SACK_NOMATCH};
-fn test_jit_add() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x3);
-fn test_jit_alu64_arith() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x2a);
-fn test_jit_alu64_bit() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x11);
-fn test_jit_alu_arith() {
-fn test_jit_alu_bit() {
-fn test_jit_arsh32_high_shift() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x4);
-fn test_jit_arsh() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0xffff8000);
-fn test_jit_arsh64() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0xfffffffffffffff8);
-fn test_jit_arsh_reg() {
-fn test_jit_be16() {
- let mem = &mut [0x11, 0x22];
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1122);
-fn test_jit_be16_high() {
- let mem = &mut [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
-fn test_jit_be32() {
- let mem = &mut [0x11, 0x22, 0x33, 0x44];
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11223344);
-fn test_jit_be32_high() {
-fn test_jit_be64() {
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1122334455667788);
-fn test_jit_call() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x0102030405);
-fn test_jit_call_memfrob() {
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x102292e2f2c0708);
-// TODO: helpers::trash_registers needs asm!().
-// Try this again once asm!() is available in stable.
-//#[test]
-//fn test_jit_call_save() {
-//let prog = &[
-//0xb7, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
-//0xb7, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
-//0xb7, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
-//0xb7, 0x09, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
-//0x85, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
-//0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-//0x4f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-//0x4f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-//0x4f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-//0x4f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-//0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-//];
-//let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
-//vm.register_helper(2, helpers::trash_registers);
-//vm.jit_compile().unwrap();
-//unsafe { assert_eq!(vm.execute_program_jit().unwrap(), 0x4321); }
-//}
-fn test_jit_div32_high_divisor() {
-fn test_jit_div32_imm() {
-fn test_jit_div32_reg() {
-fn test_jit_div64_imm() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x300000000);
-fn test_jit_div64_reg() {
-// For some register numbers, we don't emit the same instructions for handling divisions by zero,
-// which means we don't use the same offset to skip these instructions when the divisor is not
-// zero. We've had a regression because of this before, make sure we test it.
-fn test_jit_div32_highreg() {
- div32 r7, r0
- assert_eq!(vm.execute_program_jit().unwrap(), 0x2);
-fn test_jit_div64_highreg() {
- div r7, r0
-fn test_jit_early_exit() {
-// uBPF limits the number of user functions at 64. We don't.
-//fn test_jit_err_call_bad_imm() {
-#[should_panic(expected = "[JIT] Error: unknown helper function (id: 0x3f)")]
-fn test_jit_err_call_unreg() {
- vm.execute_program_jit().unwrap();
-fn test_jit_div64_by_zero_imm() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x0);
-fn test_jit_div_by_zero_imm() {
-fn test_jit_mod64_by_zero_imm() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x1);
-fn test_jit_mod_by_zero_imm() {
-fn test_jit_div64_by_zero_reg() {
-fn test_jit_div_by_zero_reg() {
-fn test_jit_mod64_by_zero_reg() {
-fn test_jit_mod_by_zero_reg() {
-// TODO SKIP: JIT disabled for this testcase (stack oob check not implemented)
-// #[test]
-// fn test_jit_err_stack_out_of_bound() {
-// let prog = &[
-// 0x72, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-// ];
-// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap();
-// vm.jit_compile().unwrap();
-// unsafe { vm.execute_program_jit().unwrap(); }
-fn test_jit_exit() {
-fn test_jit_ja() {
-fn test_jit_jeq_imm() {
-fn test_jit_jeq_reg() {
-fn test_jit_jge_imm() {
-fn test_jit_jle_imm() {
-fn test_jit_jle_reg() {
-fn test_jit_jgt_imm() {
-fn test_jit_jgt_reg() {
-fn test_jit_jlt_imm() {
-fn test_jit_jlt_reg() {
-fn test_jit_jit_bounce() {
-fn test_jit_jne_reg() {
-fn test_jit_jset_imm() {
-fn test_jit_jset_reg() {
-fn test_jit_jsge_imm() {
-fn test_jit_jsge_reg() {
-fn test_jit_jsle_imm() {
-fn test_jit_jsle_reg() {
-fn test_jit_jsgt_imm() {
-fn test_jit_jsgt_reg() {
-fn test_jit_jslt_imm() {
-fn test_jit_jslt_reg() {
-fn test_jit_jeq32_imm() {
-fn test_jit_jeq32_reg() {
-fn test_jit_jge32_imm() {
-fn test_jit_jge32_reg() {
-fn test_jit_jgt32_imm() {
-fn test_jit_jgt32_reg() {
-fn test_jit_jle32_imm() {
-fn test_jit_jle32_reg() {
-fn test_jit_jlt32_imm() {
-fn test_jit_jlt32_reg() {
-fn test_jit_jne32_imm() {
-fn test_jit_jne32_reg() {
-fn test_jit_jset32_imm() {
-fn test_jit_jset32_reg() {
-fn test_jit_jsge32_imm() {
-fn test_jit_jsge32_reg() {
-fn test_jit_jsgt32_imm() {
-fn test_jit_jsgt32_reg() {
-fn test_jit_jsle32_imm() {
-fn test_jit_jsle32_reg() {
-fn test_jit_jslt32_imm() {
-fn test_jit_jslt32_reg() {
-fn test_jit_lddw() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x1122334455667788);
-fn test_jit_lddw2() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x80000000);
-fn test_jit_ldxb_all() {
- let mem = &mut [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09];
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x9876543210);
-fn test_jit_ldxb() {
- let mem = &mut [0xaa, 0xbb, 0x11, 0xcc, 0xdd];
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11);
-fn test_jit_ldxdw() {
- 0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xcc, 0xdd,
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x8877665544332211);
-fn test_jit_ldxh_all() {
- 0x07, 0x00, 0x08, 0x00, 0x09,
-fn test_jit_ldxh_all2() {
- 0x80, 0x01, 0x00, 0x02, 0x00,
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x3ff);
-fn test_jit_ldxh() {
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x2211);
-fn test_jit_ldxh_same_reg() {
- let mem = &mut [0xff, 0xff];
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1234);
-fn test_jit_ldxw_all() {
- 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x030f0f);
-fn test_jit_ldxw() {
- let mem = &mut [0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0xcc, 0xdd];
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x44332211);
-fn test_jit_le16() {
- let mem = &mut [0x22, 0x11];
-fn test_jit_le32() {
- let mem = &mut [0x44, 0x33, 0x22, 0x11];
-fn test_jit_le64() {
- let mem = &mut [0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11];
-fn test_jit_lsh_reg() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x10);
-fn test_jit_mod() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x5);
-fn test_jit_mod32() {
-fn test_jit_mod64() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x30ba5a04);
-fn test_jit_mov() {
-fn test_jit_mul32_imm() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0xc);
-fn test_jit_mul32_reg() {
-fn test_jit_mul32_reg_overflow() {
-fn test_jit_mul64_imm() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x100000004);
-fn test_jit_mul64_reg() {
-fn test_jit_mul_loop() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x75db9c97);
-fn test_jit_neg64() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0xfffffffffffffffe);
-fn test_jit_neg() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0xfffffffe);
-fn test_jit_prime() {
-fn test_jit_rhs32() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x00ffffff);
-fn test_jit_rsh_reg() {
-fn test_jit_stack() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0xcd);
-fn test_jit_stack2() {
- assert_eq!(vm.execute_program_jit().unwrap(), 0x01020304);
-fn test_jit_stb() {
- let mem = &mut [0xaa, 0xbb, 0xff, 0xcc, 0xdd];
-fn test_jit_stdw() {
- 0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd,
-fn test_jit_sth() {
- let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd];
-fn test_jit_string_stack() {
-fn test_jit_stw() {
- let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd];
-fn test_jit_stxb() {
-fn test_jit_stxb_all() {
- let mem = &mut [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0xf0f2f3f4f5f6f7f8);
-fn test_jit_stxb_all2() {
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0xf1f9);
-fn test_jit_stxb_chain() {
- let mem = &mut [0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x2a);
-fn test_jit_stxdw() {
-fn test_jit_stxh() {
-fn test_jit_stxw() {
-fn test_jit_subnet() {
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1);
-fn test_jit_tcp_port80_match() {
-fn test_jit_tcp_port80_nomatch() {
- assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x0);
-fn test_jit_tcp_port80_nomatch_ethertype() {
-fn test_jit_tcp_port80_nomatch_proto() {
-fn test_jit_tcp_sack_match() {
- assert_eq!(vm.execute_program_jit(mem.as_mut_slice()).unwrap(), 0x1);
-fn test_jit_tcp_sack_nomatch() {
- assert_eq!(vm.execute_program_jit(mem.as_mut_slice()).unwrap(), 0x0);
@@ -1,177 +0,0 @@
-// in this file has a name in the form `test_verifier_<name>`, and corresponds to the
-// (human-readable) code in `ubpf/tree/master/tests/<name>`, available at
-// These are unit tests for the eBPF “verifier”.
-#[should_panic(expected = "[Verifier] Error: unsupported argument for LE/BE (insn #0)")]
-fn test_verifier_err_endian_size() {
- 0xdc, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
-#[should_panic(expected = "[Verifier] Error: incomplete LD_DW instruction (insn #0)")]
-fn test_verifier_err_incomplete_lddw() {
- // Note: ubpf has test-err-incomplete-lddw2, which is the same
- 0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-#[should_panic(expected = "[Verifier] Error: infinite loop")]
-fn test_verifier_err_infinite_loop() {
- ja -1
- let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
-#[should_panic(expected = "[Verifier] Error: invalid destination register (insn #0)")]
-fn test_verifier_err_invalid_reg_dst() {
- mov r11, 1
-#[should_panic(expected = "[Verifier] Error: invalid source register (insn #0)")]
-fn test_verifier_err_invalid_reg_src() {
- mov r0, r11
-#[should_panic(expected = "[Verifier] Error: jump to middle of LD_DW at #2 (insn #0)")]
-fn test_verifier_err_jmp_lddw() {
-#[should_panic(expected = "[Verifier] Error: jump out of code to #3 (insn #0)")]
-fn test_verifier_err_jmp_out() {
-#[should_panic(expected = "[Verifier] Error: program does not end with “EXIT” instruction")]
-fn test_verifier_err_no_exit() {
- mov32 r0, 0",
-fn test_verifier_err_no_exit_backward_jump() {
- ja -2",
-#[should_panic(expected = "[Verifier] Error: eBPF program length limited to 1000000, here 1000001")]
-fn test_verifier_err_too_many_instructions() {
- // uBPF uses 65637 instructions, because it sets its limit at 65636.
- // We use the classic 4096 limit from kernel, so no need to produce as many instructions.
- let mut prog = (0..(1_000_000 * ebpf::INSN_SIZE))
- .map(|x| match x % 8 {
- 0 => 0xb7,
- 1 => 0x01,
- .collect::<Vec<u8>>();
- prog.append(&mut vec![0x95, 0, 0, 0, 0, 0, 0, 0]);
-#[should_panic(expected = "[Verifier] Error: unknown eBPF opcode 0x6 (insn #0)")]
-fn test_verifier_err_unknown_opcode() {
- 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-#[should_panic(expected = "[Verifier] Error: cannot write into register r10 (insn #0)")]
-fn test_verifier_err_write_r10() {
- mov r10, 1
@@ -1,2674 +0,0 @@
-// in this file has a name in the form `test_vm_<name>`, and corresponds to the (human-readable)
-// These are unit tests for the eBPF interpreter.
-fn test_vm_add() {
-fn test_vm_alu64_arith() {
- assert_eq!(vm.execute_program().unwrap(), 0x2a);
-fn test_vm_alu64_bit() {
- assert_eq!(vm.execute_program().unwrap(), 0x11);
-fn test_vm_alu_arith() {
-fn test_vm_alu_bit() {
-fn test_vm_arsh32_high_shift() {
- assert_eq!(vm.execute_program().unwrap(), 0x4);
-fn test_vm_arsh() {
- assert_eq!(vm.execute_program().unwrap(), 0xffff8000);
-fn test_vm_arsh64() {
- assert_eq!(vm.execute_program().unwrap(), 0xfffffffffffffff8);
-fn test_vm_arsh_reg() {
-fn test_vm_arsh_imm_overflow() {
- arsh r0, 0xff20
- assert_eq!(vm.execute_program().unwrap(), 0xffffffff80000000);
-fn test_vm_arsh_reg_overflow() {
- mov r1, 0xff04
- assert_eq!(vm.execute_program().unwrap(), 0xf800000000000000);
-fn test_vm_arsh32_imm_overflow() {
- lsh32 r0, 31
- arsh32 r0, 0xff10
-fn test_vm_arsh32_reg_overflow() {
- mov32 r1, 32
- assert_eq!(vm.execute_program().unwrap(), 0x80000000);
-fn test_vm_be16() {
- let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap();
- assert_eq!(vm.execute_program(mem).unwrap(), 0x1122);
-fn test_vm_be16_high() {
-fn test_vm_be32() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x11223344);
-fn test_vm_be32_high() {
-fn test_vm_be64() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x1122334455667788);
-fn test_vm_call() {
- assert_eq!(vm.execute_program().unwrap(), 0x0102030405);
-fn test_vm_call_memfrob() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x102292e2f2c0708);
-//fn test_vm_call_save() {
-//assert_eq!(vm.execute_program().unwrap(), 0x4321);
-fn test_vm_div32_high_divisor() {
-fn test_vm_div32_imm() {
-fn test_vm_div32_reg() {
-fn test_vm_div64_imm() {
- assert_eq!(vm.execute_program().unwrap(), 0x300000000);
-fn test_vm_div64_reg() {
-fn test_vm_early_exit() {
-//fn test_vm_err_call_bad_imm() {
-#[should_panic(expected = "Error: unknown helper function (id: 0x3f)")]
-fn test_vm_err_call_unreg() {
-fn test_vm_div64_by_zero_imm() {
- assert_eq!(vm.execute_program().unwrap(), 0x0);
-fn test_vm_div_by_zero_imm() {
-fn test_vm_mod64_by_zero_imm() {
- assert_eq!(vm.execute_program().unwrap(), 0x1);
-fn test_vm_mod_by_zero_imm() {
-// Make sure we only consider the last 32 bits of the divisor.
-fn test_vm_mod_by_zero_reg_long() {
- lddw r1, 0x100000000
-fn test_vm_div64_by_zero_reg() {
-fn test_vm_div_by_zero_reg() {
-fn test_vm_div_by_zero_reg_long() {
-fn test_vm_mod64_by_zero_reg() {
-fn test_vm_mod_by_zero_reg() {
-#[should_panic(expected = "Error: out of bounds memory store (insn #1)")]
-fn test_vm_err_stack_out_of_bound() {
- stb [r10], 0
-fn test_vm_exit() {
-fn test_vm_ja() {
-fn test_vm_jeq_imm() {
-fn test_vm_jeq_reg() {
-fn test_vm_jge_imm() {
-fn test_vm_jle_imm() {
-fn test_vm_jle_reg() {
-fn test_vm_jgt_imm() {
-fn test_vm_jgt_reg() {
-fn test_vm_jlt_imm() {
-fn test_vm_jlt_reg() {
-fn test_vm_jit_bounce() {
-fn test_vm_jne_reg() {
-fn test_vm_jset_imm() {
-fn test_vm_jset_reg() {
-fn test_vm_jsge_imm() {
-fn test_vm_jsge_reg() {
-fn test_vm_jsle_imm() {
-fn test_vm_jsle_reg() {
-fn test_vm_jsgt_imm() {
-fn test_vm_jsgt_reg() {
-fn test_vm_jslt_imm() {
-fn test_vm_jslt_reg() {
-fn test_vm_jeq32_imm() {
-fn test_vm_jeq32_reg() {
-fn test_vm_jge32_imm() {
-fn test_vm_jge32_reg() {
-fn test_vm_jgt32_imm() {
-fn test_vm_jgt32_reg() {
-fn test_vm_jle32_imm() {
-fn test_vm_jle32_reg() {
-fn test_vm_jlt32_imm() {
-fn test_vm_jlt32_reg() {
-fn test_vm_jne32_imm() {
-fn test_vm_jne32_reg() {
-fn test_vm_jset32_imm() {
-fn test_vm_jset32_reg() {
-fn test_vm_jsge32_imm() {
-fn test_vm_jsge32_reg() {
-fn test_vm_jsgt32_imm() {
-fn test_vm_jsgt32_reg() {
-fn test_vm_jsle32_imm() {
-fn test_vm_jsle32_reg() {
-fn test_vm_jslt32_imm() {
-fn test_vm_jslt32_reg() {
-fn test_vm_lddw() {
- "lddw r0, 0x1122334455667788
- assert_eq!(vm.execute_program().unwrap(), 0x1122334455667788);
-fn test_vm_lddw2() {
-fn test_vm_ldxb_all() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x9876543210);
-fn test_vm_ldxb() {
-fn test_vm_ldxdw() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x8877665544332211);
-fn test_vm_ldxh_all() {
-fn test_vm_ldxh_all2() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x3ff);
-fn test_vm_ldxh() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x2211);
-fn test_vm_ldxh_same_reg() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x1234);
-fn test_vm_ldxw_all() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x030f0f);
-fn test_vm_ldxw() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x44332211);
-fn test_vm_le16() {
-fn test_vm_le32() {
-fn test_vm_le64() {
-fn test_vm_lsh_imm() {
- lsh r0, 4
- assert_eq!(vm.execute_program().unwrap(), 0x10);
-fn test_vm_lsh_reg() {
-fn test_vm_lsh32_imm() {
- lsh32 r0, 4
-fn test_vm_lsh32_reg() {
- mov32 r7, 4
- lsh32 r0, r7
-fn test_vm_lsh_imm_overflow() {
- lsh r0, 64
-fn test_vm_lsh_reg_overflow() {
- mov r7, 64
-fn test_vm_lsh32_imm_overflow() {
- lsh32 r0, 32
-fn test_vm_lsh32_reg_overflow() {
- mov32 r7, 32
-fn test_vm_mod() {
- assert_eq!(vm.execute_program().unwrap(), 0x5);
-fn test_vm_mod32() {
-fn test_vm_mod64() {
- assert_eq!(vm.execute_program().unwrap(), 0x30ba5a04);
-fn test_vm_mov() {
-fn test_vm_mul32_imm() {
- assert_eq!(vm.execute_program().unwrap(), 0xc);
-fn test_vm_mul32_reg() {
-fn test_vm_mul32_reg_overflow() {
-fn test_vm_mul64_imm() {
- assert_eq!(vm.execute_program().unwrap(), 0x100000004);
-fn test_vm_mul64_reg() {
-fn test_vm_mul_loop() {
- assert_eq!(vm.execute_program().unwrap(), 0x75db9c97);
-fn test_vm_neg64() {
- assert_eq!(vm.execute_program().unwrap(), 0xfffffffffffffffe);
-fn test_vm_neg() {
- assert_eq!(vm.execute_program().unwrap(), 0xfffffffe);
-fn test_vm_prime() {
-fn test_vm_rhs32() {
- assert_eq!(vm.execute_program().unwrap(), 0x00ffffff);
-fn test_vm_rsh_reg() {
-fn test_vm_stack() {
- assert_eq!(vm.execute_program().unwrap(), 0xcd);
-fn test_vm_stack2() {
- assert_eq!(vm.execute_program().unwrap(), 0x01020304);
-fn test_vm_stb() {
-fn test_vm_stdw() {
-// If this case is not handled properly in check_mem(), then we may overflow when adding the
-// context address and the offset, and make the thread panic with "attempt to add with overflow".
-// Check that we panic with the expected out-of-bounds error.
-// The new toolchain introduced `assert_unsafe_precondition` which panics with a different message and can't be
-// caught by `#[should_panic]`. This is why we use `#[ignore]` here.
-fn test_vm_stdw_add_overflow() {
- stdw [r2-0x1], 0x44332211
- let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(&prog), 0x00, 0x10).unwrap();
- _ = vm.execute_program(mem).unwrap();
-fn test_vm_sth() {
-fn test_vm_string_stack() {
-fn test_vm_stw() {
-fn test_vm_stxb() {
-fn test_vm_stxb_all() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0xf0f2f3f4f5f6f7f8);
-fn test_vm_stxb_all2() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0xf1f9);
-fn test_vm_stxb_chain() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x2a);
-fn test_vm_stxdw() {
-fn test_vm_stxh() {
-fn test_vm_stxw() {
-fn test_vm_subnet() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x1);
-fn test_vm_tcp_port80_match() {
-fn test_vm_tcp_port80_nomatch() {
- assert_eq!(vm.execute_program(mem).unwrap(), 0x0);
-fn test_vm_tcp_port80_nomatch_ethertype() {
-fn test_vm_tcp_port80_nomatch_proto() {
-fn test_vm_tcp_sack_match() {
- assert_eq!(vm.execute_program(mem.as_mut_slice()).unwrap(), 0x1);
-fn test_vm_tcp_sack_nomatch() {
- assert_eq!(vm.execute_program(mem.as_mut_slice()).unwrap(), 0x0);
@@ -11,7 +11,7 @@ use crate::filesystem::vfs::{FilePrivateData, FileSystem, IndexNode};
use crate::libs::casting::DowncastArc;
use crate::libs::spinlock::SpinLockGuard;
use crate::perf::util::PerfProbeArgs;
-use crate::perf::PerfEventOps;
+use crate::perf::{BasicPerfEbpfCallBack, PerfEventOps};
use alloc::boxed::Box;
use alloc::string::String;
use alloc::sync::Arc;
@@ -19,7 +19,7 @@ use alloc::vec::Vec;
use core::any::Any;
use core::fmt::Debug;
use kprobe::{CallBackFunc, ProbeArgs};
-use rbpf::EbpfVmRawOwned;
+use rbpf::EbpfVmRaw;
use system_error::SystemError;
#[derive(Debug)]
pub struct KprobePerfEvent {
@@ -40,33 +40,49 @@ impl KprobePerfEvent {
.downcast_arc::<BpfProg>()
.ok_or(SystemError::EINVAL)?;
let prog_slice = file.insns();
- let mut vm = EbpfVmRawOwned::new(Some(prog_slice.to_vec())).map_err(|e| {
+
+ let prog_slice =
+ unsafe { core::slice::from_raw_parts(prog_slice.as_ptr(), prog_slice.len()) };
+ let mut vm = EbpfVmRaw::new(Some(prog_slice)).map_err(|e| {
log::error!("create ebpf vm failed: {:?}", e);
SystemError::EINVAL
})?;
- vm.register_helper_set(BPF_HELPER_FUN_SET.get())
- .map_err(|_| SystemError::EINVAL)?;
+ for (id, f) in BPF_HELPER_FUN_SET.get() {
+ vm.register_helper(*id, *f)
+ .map_err(|_| SystemError::EINVAL)?;
+ }
// create a callback to execute the ebpf prog
- let callback = Box::new(KprobePerfCallBack::new(file, vm));
+ let callback;
+ #[cfg(target_arch = "x86_64")]
+ {
+ use crate::perf::JITMem;
+ log::info!("Using JIT compilation for BPF program on x86_64 architecture");
+ let jit_mem = Box::new(JITMem::new());
+ let jit_mem = Box::leak(jit_mem);
+ let jit_mem_addr = core::ptr::from_ref::<JITMem>(jit_mem) as usize;
+ vm.set_jit_exec_memory(jit_mem).unwrap();
+ vm.jit_compile().unwrap();
+ let basic_callback = BasicPerfEbpfCallBack::new(file, vm, jit_mem_addr);
+ callback = Box::new(KprobePerfCallBack(basic_callback));
+ #[cfg(not(target_arch = "x86_64"))]
+ vm.register_allowed_memory(0..u64::MAX);
+ let basic_callback = BasicPerfEbpfCallBack::new(file, vm);
// update callback for kprobe
self.kprobe.write().update_event_callback(callback);
Ok(())
}
-pub struct KprobePerfCallBack {
- _bpf_prog_file: Arc<BpfProg>,
- vm: EbpfVmRawOwned,
-impl KprobePerfCallBack {
- fn new(bpf_prog_file: Arc<BpfProg>, vm: EbpfVmRawOwned) -> Self {
- _bpf_prog_file: bpf_prog_file,
- vm,
+pub struct KprobePerfCallBack(BasicPerfEbpfCallBack);
impl CallBackFunc for KprobePerfCallBack {
fn call(&self, trap_frame: &dyn ProbeArgs) {
@@ -78,10 +94,7 @@ impl CallBackFunc for KprobePerfCallBack {
size_of::<KProbeContext>(),
)
};
- let res = self.vm.execute_program(probe_context);
- if res.is_err() {
- log::error!("kprobe callback error: {:?}", res);
+ self.0.call(probe_context);
@@ -3,6 +3,8 @@ mod kprobe;
mod tracepoint;
mod util;
+use crate::arch::MMArch;
+use crate::bpf::prog::BpfProg;
use crate::filesystem::epoll::{event_poll::EventPoll, EPollEventType, EPollItem};
use crate::filesystem::page_cache::PageCache;
use crate::filesystem::vfs::file::{File, FileMode};
@@ -15,8 +17,11 @@ use crate::include::bindings::linux_bpf::{
use crate::libs::spinlock::{SpinLock, SpinLockGuard};
+use crate::mm::allocator::page_frame::{
+ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame,
+};
use crate::mm::fault::{PageFaultHandler, PageFaultMessage};
-use crate::mm::VmFaultReason;
+use crate::mm::{MemoryManagementArch, VirtAddr, VmFaultReason};
use crate::perf::bpf::BpfPerfEvent;
use crate::perf::util::{PerfEventIoc, PerfEventOpenFlags, PerfProbeArgs, PerfProbeConfig};
use crate::process::ProcessManager;
@@ -30,10 +35,11 @@ use alloc::vec::Vec;
use core::ffi::c_void;
-use core::ops::Deref;
+use core::ops::{Deref, DerefMut};
use intertrait::{CastFrom, CastFromSync};
use log::info;
use num_traits::FromPrimitive;
type Result<T> = core::result::Result<T, SystemError>;
@@ -55,6 +61,103 @@ pub trait PerfEventOps: Send + Sync + Debug + CastFromSync + CastFrom + IndexNod
fn readable(&self) -> bool;
+pub struct JITMem {
+ virt_addr: VirtAddr,
+}
+impl JITMem {
+ pub fn new() -> Self {
+ let vaddr = unsafe {
+ let (paddr, _count) =
+ allocate_page_frames(PageFrameCount::new(1)).expect("JITMem alloc failed");
+ MMArch::phys_2_virt(paddr).unwrap()
+ };
+ Self { virt_addr: vaddr }
+impl Deref for JITMem {
+ type Target = [u8];
+ fn deref(&self) -> &Self::Target {
+ unsafe {
+ let ptr = self.virt_addr.as_ptr();
+ core::slice::from_raw_parts(ptr, 4096)
+impl DerefMut for JITMem {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ core::slice::from_raw_parts_mut(ptr, 4096)
+impl Drop for JITMem {
+ fn drop(&mut self) {
+ let paddr = MMArch::virt_2_phys(self.virt_addr).expect("JITMem drop failed");
+ let count = PageFrameCount::new(1);
+ deallocate_page_frames(PhysPageFrame::new(paddr), count);
+pub struct BasicPerfEbpfCallBack {
+ _bpf_prog_file: Arc<BpfProg>,
+ vm: EbpfVmRaw<'static>,
+ jit_mem_ptr: usize,
+unsafe impl Send for BasicPerfEbpfCallBack {}
+unsafe impl Sync for BasicPerfEbpfCallBack {}
+impl BasicPerfEbpfCallBack {
+ fn new(bpf_prog_file: Arc<BpfProg>, vm: EbpfVmRaw<'static>) -> Self {
+ Self {
+ _bpf_prog_file: bpf_prog_file,
+ vm,
+ fn new(bpf_prog_file: Arc<BpfProg>, vm: EbpfVmRaw<'static>, jit_mem_ptr: usize) -> Self {
+ jit_mem_ptr,
+ pub fn call(&self, entry: &mut [u8]) {
+ let res = if cfg!(target_arch = "x86_64") {
+ unsafe { self.vm.execute_program_jit(entry) }
+ } else {
+ self.vm.execute_program(entry)
+ if res.is_err() {
+ log::error!("kprobe callback error: {:?}", res);
+impl Drop for BasicPerfEbpfCallBack {
+ let jit_mem = &mut *(self.jit_mem_ptr as *mut JITMem);
+ let jit_mem = Box::from_raw(jit_mem);
+ drop(jit_mem);
pub struct PerfEventInode {
event: Box<dyn PerfEventOps>,
@@ -5,6 +5,7 @@ use crate::filesystem::page_cache::PageCache;
use crate::libs::spinlock::SpinLock;
use crate::perf::util::PerfProbeConfig;
+use crate::perf::{BasicPerfEbpfCallBack, JITMem};
use crate::tracepoint::{TracePoint, TracePointCallBackFunc};
use crate::{
filesystem::vfs::{file::File, FilePrivateData, FileSystem, IndexNode},
@@ -16,7 +17,7 @@ use alloc::sync::Arc;
use alloc::{string::String, vec::Vec};
use core::sync::atomic::AtomicUsize;
@@ -74,29 +75,14 @@ impl IndexNode for TracepointPerfEvent {
-pub struct TracePointPerfCallBack {
-impl TracePointPerfCallBack {
+pub struct TracePointPerfCallBack(BasicPerfEbpfCallBack);
impl TracePointCallBackFunc for TracePointPerfCallBack {
fn call(&self, entry: &[u8]) {
// ebpf needs a mutable slice
let entry =
unsafe { core::slice::from_raw_parts_mut(entry.as_ptr() as *mut u8, entry.len()) };
- let res = self.vm.execute_program(entry);
- log::error!("tracepoint callback error: {:?}", res);
+ self.0.call(entry);
@@ -109,15 +95,39 @@ impl PerfEventOps for TracepointPerfEvent {
- let callback = Box::new(TracePointPerfCallBack::new(file, vm));
+ callback = Box::new(TracePointPerfCallBack(basic_callback));
let id = CALLBACK_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
self.tp.register_raw_callback(id, callback);
@@ -84,7 +84,6 @@ trigger_files = ["kernel/src/virt", "kernel/src/arch/x86_64/kvm"]
[autolabel."T-Obs and Test"]
trigger_files = [
- "/kernel/crates/rbpf",
"/kernel/crates/kprobe",
"/kernel/src/debug/kprobe",
"/kernel/src/bpf",
@@ -167,7 +166,6 @@ infra = ["@dragonos/infra"]
"/kernel/src/arch/x86_64/kvm" = ["virtulization"]
"/kernel/src/arch/x86_64" = ["x86_64"]
"/kernel/src/arch/riscv64" = ["riscv64"]
-"/kernel/crates/rbpf" = ["sig-obs"]
"/kernel/crates/kprobe" = ["sig-obs"]
"/kernel/src/debug/kprobe" = ["sig-obs"]
"/kernel/src/bpf" = ["sig-obs"]