소스 검색

rbpf: initial commit, interpreter and JIT work

Quentin Monnet 8 년 전
커밋
44c872d10f
16개의 변경된 파일6617개의 추가작업 그리고 0개의 파일을 삭제
  1. 2 0
      .gitignore
  2. 7 0
      Cargo.toml
  3. 202 0
      LICENSE-APACHE
  4. 25 0
      LICENSE-MIT
  5. 308 0
      README.md
  6. BIN
      rbpf.png
  7. BIN
      rbpf_256.png
  8. 219 0
      src/ebpf.rs
  9. 102 0
      src/helpers.rs
  10. 862 0
      src/jit.rs
  11. 533 0
      src/lib.rs
  12. 243 0
      src/verifier.rs
  13. 305 0
      tests/misc.rs
  14. 1872 0
      tests/ubpf_jit_x86_64.rs
  15. 166 0
      tests/ubpf_verifier.rs
  16. 1771 0
      tests/ubpf_vm.rs

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+target
+Cargo.lock

+ 7 - 0
Cargo.toml

@@ -0,0 +1,7 @@
+[project]
+name = "rbpf"
+version = "0.0.1"
+authors = ["Quentin Monnet <quentin.monnet@6wind.com>"]
+
+[dependencies]
+libc = "0.2.0"

+ 202 - 0
LICENSE-APACHE

@@ -0,0 +1,202 @@
+
+                                 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
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (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.

+ 25 - 0
LICENSE-MIT

@@ -0,0 +1,25 @@
+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.

+ 308 - 0
README.md

@@ -0,0 +1,308 @@
+# rbpf
+
+![](rbpf_256.png)
+
+Rust (user-space) virtual machine for eBPF
+
+## 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 as well as a JIT-compiler for eBPF programs.
+
+It is based on Rich Lane's [uBPF software](https://github.com/iovisor/ubpf/),
+which does nearly the same, but is written in C.
+
+## Example uses
+
+### Simple example
+
+This comes from the unit test `test_vm_add`.
+
+```rust
+extern crate rbpf;
+
+fn main() {
+
+    // This is the eBPF program, in the form of bytecode instructions.
+    let prog = vec![
+        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(&prog);
+
+    // Execute (interpret) the program. No argument required for this VM.
+    assert_eq!(vm.prog_exec(), 0x3);
+}
+```
+
+### With JIT, on packet data
+
+This comes from the unit test `test_jit_ldxh`.
+
+```rust
+extern crate rbpf;
+
+fn main() {
+    let prog = vec![
+        0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxh r0, [r1+2]
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  // exit
+    ];
+
+    // Let's use some data.
+    let mut mem = vec![
+        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(&prog);
+
+    // This time we JIT-compile the program.
+    vm.jit_compile();
+
+    // 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.
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x11);
+}
+```
+### Using a metadata buffer
+
+This comes from the unit test `test_jit_mbuff` and derives from the unit test
+`test_jit_ldxh`.
+
+```rust
+extern crate rbpf;
+
+fn main() {
+    let prog = vec![
+        // 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
+    ];
+    let mut mem = vec![
+        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 mbuff = vec![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(&prog);
+
+    // Here again we JIT-compile the program.
+    vm.jit_compile();
+
+    // Here we must provide both a reference to the packet data, and to the
+    // metadata buffer we use.
+    assert_eq!(vm.prog_exec_jit(&mut mem, mbuff), 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.
+
+```toml
+[dependencies]
+…
+byteorder = "0.5.3"
+elf = "0.0.10"
+```
+
+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 takes
+in charger the buffer update so that we only have to provide a reference to the
+packet data for each run of the program.
+
+```rust
+extern crate byteorder;
+extern crate elf;
+use std::path::PathBuf;
+
+extern crate rbpf;
+use rbpf::helpers;
+
+fn main() {
+    // 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 = "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),
+    };
+
+    // 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 ref prog = &text_scn.data;
+
+    // This is our data: a real packet, starting with Ethernet header
+    let mut packet = vec![
+        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,
+        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(&prog, 0x40, 0x50);
+
+    // We register a helper function, that can be called by the program, into
+    // the VM.
+    vm.register_helper(helpers::BPF_TRACE_PRINTF_IDX, helpers::bpf_trace_printf);
+
+    // 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.prog_exec(&mut packet);
+    println!("Program returned: {:?} ({:#x})", res, res);
+}
+```
+
+### API
+
+Most of the API is displayed in the above examples.
+
+```
+TBD
+```
+
+## 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.
+
+## 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.
+
+### 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 today) for example, you may be interested in the
+[bpfjit crate](https://crates.io/crates/bpfjit) written by Alexander Polakov
+instead.
+
+### 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 emphasize on safety. Yet to have the eBPF VM work, some
+“unsafe” blocks of code are used. The VM, taken as an eBPF interpreter, can
+`panic!()` 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.
+
+### Where is the documentation?
+
+Comments and Rust documentation in the source code.
+
+## 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/master/LICENSE-APACHE)
+and [LICENSE-MIT](https://github.com/qmonnet/rbpf/blob/master/LICENSE-MIT) for
+details.
+
+## Inspired by
+
+* [uBPF](https://github.com/iovisor/ubpf), a C user-space implementation of an
+  eBPF virtual machine, with a JIT-compiler (and also including assembler and
+  disassembler from and to 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_](http://www.jonathanturner.org/2015/12/building-a-simple-jit-in-rust.html),
+  by Jonathan 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
+
+* Kernel documentation about BPF: [Documentation/networking/filter.txt
+  file](https://www.kernel.org/doc/Documentation/networking/filter.txt)
+
+* [_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)

BIN
rbpf.png


BIN
rbpf_256.png


+ 219 - 0
src/ebpf.rs

@@ -0,0 +1,219 @@
+// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+use std;
+
+pub const PROG_MAX_INSNS: usize = 4096;
+pub const INSN_SIZE: usize = 8;
+pub const PROG_MAX_SIZE: usize = PROG_MAX_INSNS * INSN_SIZE;
+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:
+pub const BPF_LD    : u8 = 0x00;
+pub const BPF_LDX   : u8 = 0x01;
+pub const BPF_ST    : u8 = 0x02;
+pub const BPF_STX   : u8 = 0x03;
+pub const BPF_ALU   : u8 = 0x04;
+pub const BPF_JMP   : u8 = 0x05;
+// [ class 6 unused, reserved for future use ]
+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:
+pub const BPF_W     : u8 = 0x00; // word
+pub const BPF_H     : u8 = 0x08; // half-word
+pub const BPF_B     : u8 = 0x10; // byte
+pub const BPF_DW    : u8 = 0x18; // double word
+
+// Mode modifiers:
+pub const BPF_IMM   : u8 = 0x00;
+pub const BPF_ABS   : u8 = 0x20;
+pub const BPF_IND   : u8 = 0x40;
+pub const BPF_MEM   : u8 = 0x60;
+// [ 0x80 reserved ]
+// [ 0xa0 reserved ]
+pub const BPF_XADD  : u8 = 0xc0; // exclusive add
+
+// For arithmetic (BPF_ALU/BPF_ALU64) and jump (BPF_JMP) instructions:
+// +----------------+--------+--------+
+// |     4 bits     |1 b.|   3 bits   |
+// | operation code | src| insn class |
+// +----------------+----+------------+
+// (MSB)                          (LSB)
+
+// Source modifiers:
+pub const BPF_K     : u8 = 0x00; // use 32-bit immediate as source operand
+pub const BPF_X     : u8 = 0x08; // use `src` register as source operand
+
+// Operation codes -- BPF_ALU or BPF_ALU64 classes:
+pub const BPF_ADD   : u8 = 0x00;
+pub const BPF_SUB   : u8 = 0x10;
+pub const BPF_MUL   : u8 = 0x20;
+pub const BPF_DIV   : u8 = 0x30;
+pub const BPF_OR    : u8 = 0x40;
+pub const BPF_AND   : u8 = 0x50;
+pub const BPF_LSH   : u8 = 0x60;
+pub const BPF_RSH   : u8 = 0x70;
+pub const BPF_NEG   : u8 = 0x80;
+pub const BPF_MOD   : u8 = 0x90;
+pub const BPF_XOR   : u8 = 0xa0;
+pub const BPF_MOV   : u8 = 0xb0; // mov reg to reg
+pub const BPF_ARSH  : u8 = 0xc0; // sign extending shift right
+pub const BPF_END   : u8 = 0xd0; // endianness conversion
+
+// Operation codes -- BPF_JMP class:
+pub const BPF_JA    : u8 = 0x00;
+pub const BPF_JEQ   : u8 = 0x10;
+pub const BPF_JGT   : u8 = 0x20;
+pub const BPF_JGE   : u8 = 0x30;
+pub const BPF_JSET  : u8 = 0x40;
+pub const BPF_JNE   : u8 = 0x50; // jump !=
+pub const BPF_JSGT  : u8 = 0x60; // signed '>'
+pub const BPF_JSGE  : u8 = 0x70; // signed '>='
+pub const BPF_CALL  : u8 = 0x80; // function call
+pub const BPF_EXIT  : u8 = 0x90; // function return
+
+// 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.)
+
+pub const LD_ABS_B   : u8 = BPF_LD    | BPF_ABS | BPF_B;
+pub const LD_ABS_H   : u8 = BPF_LD    | BPF_ABS | BPF_H;
+pub const LD_ABS_W   : u8 = BPF_LD    | BPF_ABS | BPF_W;
+pub const LD_ABS_DW  : u8 = BPF_LD    | BPF_ABS | BPF_DW;
+pub const LD_IND_B   : u8 = BPF_LD    | BPF_IND | BPF_B;
+pub const LD_IND_H   : u8 = BPF_LD    | BPF_IND | BPF_H;
+pub const LD_IND_W   : u8 = BPF_LD    | BPF_IND | BPF_W;
+pub const LD_IND_DW  : u8 = BPF_LD    | BPF_IND | BPF_DW;
+
+pub const LD_DW_IMM  : u8 = BPF_LD    | BPF_IMM | BPF_DW;
+pub const LD_B_REG   : u8 = BPF_LDX   | BPF_MEM | BPF_B;
+pub const LD_H_REG   : u8 = BPF_LDX   | BPF_MEM | BPF_H;
+pub const LD_W_REG   : u8 = BPF_LDX   | BPF_MEM | BPF_W;
+pub const LD_DW_REG  : u8 = BPF_LDX   | BPF_MEM | BPF_DW;
+pub const ST_B_IMM   : u8 = BPF_ST    | BPF_MEM | BPF_B;
+pub const ST_H_IMM   : u8 = BPF_ST    | BPF_MEM | BPF_H;
+pub const ST_W_IMM   : u8 = BPF_ST    | BPF_MEM | BPF_W;
+pub const ST_DW_IMM  : u8 = BPF_ST    | BPF_MEM | BPF_DW;
+pub const ST_B_REG   : u8 = BPF_STX   | BPF_MEM | BPF_B;
+pub const ST_H_REG   : u8 = BPF_STX   | BPF_MEM | BPF_H;
+pub const ST_W_REG   : u8 = BPF_STX   | BPF_MEM | BPF_W;
+pub const ST_DW_REG  : u8 = BPF_STX   | BPF_MEM | BPF_DW;
+
+pub const ST_W_XADD  : u8 = BPF_STX   | BPF_XADD | BPF_W;
+pub const ST_DW_XADD : u8 = BPF_STX   | BPF_XADD | BPF_DW;
+
+pub const ADD32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_ADD;
+pub const ADD32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_ADD;
+pub const SUB32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_SUB;
+pub const SUB32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_SUB;
+pub const MUL32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_MUL;
+pub const MUL32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_MUL;
+pub const DIV32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_DIV;
+pub const DIV32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_DIV;
+pub const OR32_IMM   : u8 = BPF_ALU   | BPF_K   | BPF_OR;
+pub const OR32_REG   : u8 = BPF_ALU   | BPF_X   | BPF_OR;
+pub const AND32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_AND;
+pub const AND32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_AND;
+pub const LSH32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_LSH;
+pub const LSH32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_LSH;
+pub const RSH32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_RSH;
+pub const RSH32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_RSH;
+pub const NEG32      : u8 = BPF_ALU   | BPF_NEG;
+pub const MOD32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_MOD;
+pub const MOD32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_MOD;
+pub const XOR32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_XOR;
+pub const XOR32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_XOR;
+pub const MOV32_IMM  : u8 = BPF_ALU   | BPF_K   | BPF_MOV;
+pub const MOV32_REG  : u8 = BPF_ALU   | BPF_X   | BPF_MOV;
+pub const ARSH32_IMM : u8 = BPF_ALU   | BPF_K   | BPF_ARSH;
+pub const ARSH32_REG : u8 = BPF_ALU   | BPF_X   | BPF_ARSH;
+
+pub const LE         : u8 = BPF_ALU   | BPF_K   | BPF_END;
+pub const BE         : u8 = BPF_ALU   | BPF_X   | BPF_END;
+
+pub const ADD64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_ADD;
+pub const ADD64_REG  : u8 = BPF_ALU64 | BPF_X   | BPF_ADD;
+pub const SUB64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_SUB;
+pub const SUB64_REG  : u8 = BPF_ALU64 | BPF_X   | BPF_SUB;
+pub const MUL64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_MUL;
+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;
+pub const OR64_IMM   : u8 = BPF_ALU64 | BPF_K   | BPF_OR;
+pub const OR64_REG   : u8 = BPF_ALU64 | BPF_X   | BPF_OR;
+pub const AND64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_AND;
+pub const AND64_REG  : u8 = BPF_ALU64 | BPF_X   | BPF_AND;
+pub const LSH64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_LSH;
+pub const LSH64_REG  : u8 = BPF_ALU64 | BPF_X   | BPF_LSH;
+pub const RSH64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_RSH;
+pub const RSH64_REG  : u8 = BPF_ALU64 | BPF_X   | BPF_RSH;
+pub const NEG64      : u8 = BPF_ALU64 | BPF_NEG;
+pub const MOD64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_MOD;
+pub const MOD64_REG  : u8 = BPF_ALU64 | BPF_X   | BPF_MOD;
+pub const XOR64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_XOR;
+pub const XOR64_REG  : u8 = BPF_ALU64 | BPF_X   | BPF_XOR;
+pub const MOV64_IMM  : u8 = BPF_ALU64 | BPF_K   | BPF_MOV;
+pub const MOV64_REG  : u8 = BPF_ALU64 | BPF_X   | BPF_MOV;
+pub const ARSH64_IMM : u8 = BPF_ALU64 | BPF_K   | BPF_ARSH;
+pub const ARSH64_REG : u8 = BPF_ALU64 | BPF_X   | BPF_ARSH;
+
+pub const JA         : u8 = BPF_JMP   | BPF_JA;
+pub const JEQ_IMM    : u8 = BPF_JMP   | BPF_K   | BPF_JEQ;
+pub const JEQ_REG    : u8 = BPF_JMP   | BPF_X   | BPF_JEQ;
+pub const JGT_IMM    : u8 = BPF_JMP   | BPF_K   | BPF_JGT;
+pub const JGT_REG    : u8 = BPF_JMP   | BPF_X   | BPF_JGT;
+pub const JGE_IMM    : u8 = BPF_JMP   | BPF_K   | BPF_JGE;
+pub const JGE_REG    : u8 = BPF_JMP   | BPF_X   | BPF_JGE;
+pub const JSET_IMM   : u8 = BPF_JMP   | BPF_K   | BPF_JSET;
+pub const JSET_REG   : u8 = BPF_JMP   | BPF_X   | BPF_JSET;
+pub const JNE_IMM    : u8 = BPF_JMP   | BPF_K   | BPF_JNE;
+pub const JNE_REG    : u8 = BPF_JMP   | BPF_X   | BPF_JNE;
+pub const JSGT_IMM   : u8 = BPF_JMP   | BPF_K   | BPF_JSGT;
+pub const JSGT_REG   : u8 = BPF_JMP   | BPF_X   | BPF_JSGT;
+pub const JSGE_IMM   : u8 = BPF_JMP   | BPF_K   | BPF_JSGE;
+pub const JSGE_REG   : u8 = BPF_JMP   | BPF_X   | BPF_JSGE;
+
+pub const CALL       : u8 = BPF_JMP   | BPF_CALL;
+pub const TAIL_CALL  : u8 = BPF_JMP   | BPF_X | BPF_CALL;
+pub const EXIT       : u8 = BPF_JMP   | BPF_EXIT;
+
+// Used in JIT
+pub const BPF_CLS_MASK    : u8 = 0x07;
+pub const BPF_ALU_OP_MASK : u8 = 0xf0;
+
+#[derive(Debug)]
+pub struct Insn {
+    pub opc: u8,
+    pub dst: u8,
+    pub src: u8,
+    pub off: i16,
+    pub imm: i32,
+}
+
+// Get the nth instruction of an eBPF program
+// idx is the index (number) of the instruction.
+pub fn get_insn(prog: &std::vec::Vec<u8>, idx: usize) -> Insn {
+    // TODO panic if size problem? Should be checked by verifier, though
+    let insn = Insn {
+        opc:  prog[INSN_SIZE * idx],
+        dst:  prog[INSN_SIZE * idx + 1] & 0x0f,
+        src: (prog[INSN_SIZE * idx + 1] & 0xf0) >> 4,
+        off: unsafe { let x = prog.as_ptr().offset((INSN_SIZE * idx + 2) as isize) as *const i16; *x },
+        imm: unsafe { let x = prog.as_ptr().offset((INSN_SIZE * idx + 4) as isize) as *const i32; *x },
+    };
+    insn
+}

+ 102 - 0
src/helpers.rs

@@ -0,0 +1,102 @@
+// Copyright 2015 Big Switch Networks, Inc
+//      (Algorithms for uBPF helpers, originally in C)
+// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
+//      (Translation to Rust, other helpers)
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+use std::u64;
+
+// Helpers associated to kernel helpers
+// See also linux/include/uapi/linux/bpf.h in Linux kernel sources.
+
+// bpf_trace_printk()
+// No side effect: just print arg3, arg4 and arg5 to standard output.
+pub const BPF_TRACE_PRINTF_IDX: u32 = 6;
+
+#[allow(dead_code)]
+#[allow(unused_variables)]
+pub fn bpf_trace_printf (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
+    println!("bpf_trace_printf: {:#x}, {:#x}, {:#x}", arg3, arg4, arg5);
+    0
+}
+
+
+// Helpers coming from uBPF <https://github.com/iovisor/ubpf/blob/master/vm/test.c>
+
+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
+}
+
+#[allow(unused_variables)]
+pub fn memfrob (ptr: u64, len: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
+    for i in 0..len {
+        unsafe {
+            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
+// }
+
+#[allow(dead_code)]
+#[allow(unused_variables)]
+pub fn sqrti (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
+    // Warning: untested
+    (arg1 as f64).sqrt() as u64
+}
+
+#[allow(dead_code)]
+#[allow(unused_variables)]
+pub fn strcmp (arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: 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;
+    unsafe {
+        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
+        } else {
+            (b_val - a_val) as u64
+        }
+    }
+}

+ 862 - 0
src/jit.rs

@@ -0,0 +1,862 @@
+// Derived from uBPF <https://github.com/iovisor/ubpf>
+// Copyright 2015 Big Switch Networks, Inc
+//      (uBPF: JIT algorithm, originally in C)
+// Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com>
+//      (Translation to Rust, MetaBuff addition)
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+use std;
+use std::mem;
+use std::collections::HashMap;
+use std::fmt::{Error, Formatter};
+use std::ops::{Index, IndexMut};
+
+use ebpf;
+
+extern crate libc;
+
+extern {
+    fn memset(s: *mut libc::c_void, c: libc::uint32_t, n: libc::size_t) -> *mut libc::c_void;
+}
+const PAGE_SIZE: usize = 4096;
+
+// 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;
+const TARGET_PC_DIV_BY_ZERO:  isize = TARGET_OFFSET + 2;
+
+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
+];
+
+// 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 {
+    ( $jit:ident, $data:tt, $t:ty ) => {{
+        let size = mem::size_of::<$t>() as usize;
+        assert!($jit.offset + size <= $jit.contents.len());
+        unsafe {
+            let mut ptr = $jit.contents.as_ptr().offset($jit.offset as isize) as *mut $t;
+            *ptr = $data as $t;
+        }
+        $jit.offset += size;
+    }}
+}
+
+#[inline]
+fn emit1 (jit: &mut JitMemory, data: u8) {
+    emit_bytes!(jit, data, u8);
+}
+
+#[inline]
+fn emit2 (jit: &mut JitMemory, data: u16) {
+    emit_bytes!(jit, data, u16);
+}
+
+#[inline]
+fn emit4 (jit: &mut JitMemory, data: u32) {
+    emit_bytes!(jit, data, u32);
+}
+
+#[inline]
+fn emit8 (jit: &mut JitMemory, data: u64) {
+    emit_bytes!(jit, data, u64);
+}
+
+#[inline]
+fn emit_jump_offset (jit: &mut JitMemory, target_pc: isize) {
+    let jump = Jump { offset_loc: jit.offset, target_pc: target_pc };
+    jit.jumps.push(jump);
+    emit4(jit, 0);
+}
+
+#[inline]
+fn emit_modrm (jit: &mut JitMemory, modrm: u8, r: u8, m: u8) {
+    assert!((modrm | 0xc0) == 0xc0);
+    emit1(jit, (modrm & 0xc0) | ((r & 0b111) << 3) | (m & 0b111));
+}
+
+#[inline]
+fn emit_modrm_reg2reg (jit: &mut JitMemory, r: u8, m: u8) {
+    emit_modrm(jit, 0xc0, r, m);
+}
+
+#[inline]
+fn emit_modrm_and_displacement (jit: &mut JitMemory, r: u8, m: u8, d: i32) {
+    if d == 0 && (m & 0b111) != RBP {
+        emit_modrm(jit, 0x00, r, m);
+    } else if d >= -128 && d <= 127 {
+        emit_modrm(jit, 0x40, r, m);
+        emit1(jit, d as u8);
+    } else {
+        emit_modrm(jit, 0x80, r, m);
+        emit4(jit, d as u32);
+    }
+}
+
+#[inline]
+fn emit_rex (jit: &mut JitMemory, w: u8, r: u8, x: u8, b: u8) {
+    assert!((w | 1) == 1);
+    assert!((r | 1) == 1);
+    assert!((x | 1) == 1);
+    assert!((b | 1) == 1);
+    emit1(jit, 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.
+#[inline]
+fn emit_basic_rex (jit: &mut JitMemory, w: u8, src: u8, dst: u8) {
+    if w != 0 || (src & 0b1000) != 0 || (dst & 0b1000) != 0 {
+        let is_masked = | val, mask | { match val & mask {
+            0 => 0,
+            _ => 1
+        }};
+        emit_rex(jit, w, is_masked(src, 8), 0, is_masked(dst, 8));
+    }
+}
+
+#[inline]
+fn emit_push (jit: &mut JitMemory, r: u8) {
+    emit_basic_rex(jit, 0, 0, r);
+    emit1(jit, 0x50 | (r & 0b111));
+}
+
+#[inline]
+fn emit_pop (jit: &mut JitMemory, r: u8) {
+    emit_basic_rex(jit, 0, 0, r);
+    emit1(jit, 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
+#[inline]
+fn emit_alu32 (jit: &mut JitMemory, op: u8, src: u8, dst: u8) {
+    emit_basic_rex(jit, 0, src, dst);
+    emit1(jit, op);
+    emit_modrm_reg2reg(jit, src, dst);
+}
+
+// REX prefix, ModRM byte, and 32-bit immediate
+#[inline]
+fn emit_alu32_imm32 (jit: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i32) {
+    emit_alu32(jit, op, src, dst);
+    emit4(jit, imm as u32);
+}
+
+// REX prefix, ModRM byte, and 8-bit immediate
+#[inline]
+fn emit_alu32_imm8 (jit: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i8) {
+    emit_alu32(jit, op, src, dst);
+    emit1(jit, imm as u8);
+}
+
+// REX.W prefix and ModRM byte
+// We use the MR encoding when there is a choice
+// 'src' is often used as an opcode extension
+#[inline]
+fn emit_alu64 (jit: &mut JitMemory, op: u8, src: u8, dst: u8) {
+    emit_basic_rex(jit, 1, src, dst);
+    emit1(jit, op);
+    emit_modrm_reg2reg(jit, src, dst);
+}
+
+// REX.W prefix, ModRM byte, and 32-bit immediate
+#[inline]
+fn emit_alu64_imm32 (jit: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i32) {
+    emit_alu64(jit, op, src, dst);
+    emit4(jit, imm as u32);
+}
+
+// REX.W prefix, ModRM byte, and 8-bit immediate
+#[inline]
+fn emit_alu64_imm8 (jit: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i8) {
+    emit_alu64(jit, op, src, dst);
+    emit1(jit, imm as u8);
+}
+
+// Register to register mov
+#[inline]
+fn emit_mov (jit: &mut JitMemory, src: u8, dst: u8) {
+    emit_alu64(jit, 0x89, src, dst);
+}
+
+#[inline]
+fn emit_cmp_imm32 (jit: &mut JitMemory, dst: u8, imm: i32) {
+    emit_alu64_imm32(jit, 0x81, 7, dst, imm);
+}
+
+#[inline]
+fn emit_cmp (jit: &mut JitMemory, src: u8, dst: u8) {
+    emit_alu64(jit, 0x39, src, dst);
+}
+
+#[inline]
+fn emit_jcc (jit: &mut JitMemory, code: u8, target_pc: isize) {
+    emit1(jit, 0x0f);
+    emit1(jit, code);
+    emit_jump_offset(jit, target_pc);
+}
+
+#[inline]
+fn emit_jmp (jit: &mut JitMemory, target_pc: isize) {
+    emit1(jit, 0xe9);
+    emit_jump_offset(jit, target_pc);
+}
+
+#[inline]
+fn set_anchor(jit: &mut JitMemory, target: isize) {
+    jit.special_targets.insert(target, jit.offset);
+}
+
+// Load [src + offset] into dst
+#[inline]
+fn emit_load (jit: &mut JitMemory, size: OperandSize, src: u8, dst: u8, offset: i32) {
+    let data = match size {
+        OperandSize::S64 => 1,
+        _ => 0
+    };
+    emit_basic_rex(jit, data, dst, src);
+
+    match size {
+        OperandSize::S8 => {
+            // movzx
+            emit1(jit, 0x0f);
+            emit1(jit, 0xb6);
+        },
+        OperandSize::S16 => {
+            // movzx
+            emit1(jit, 0x0f);
+            emit1(jit, 0xb7);
+        },
+        OperandSize::S32 | OperandSize::S64 => {
+            // mov
+            emit1(jit, 0x8b);
+        }
+    }
+
+    emit_modrm_and_displacement(jit, dst, src, offset);
+}
+
+// Load sign-extended immediate into register
+#[inline]
+fn emit_load_imm (jit: &mut JitMemory, dst: u8, imm: i64) {
+    if imm >= std::i32::MIN as i64 && imm <= std::i32::MAX as i64 {
+        emit_alu64_imm32(jit, 0xc7, 0, dst, imm as i32);
+    } else {
+        // movabs $imm,dst
+        emit_basic_rex(jit, 1, 0, dst);
+        emit1(jit, 0xb8 | (dst & 0b111));
+        emit8(jit, imm as u64);
+    }
+}
+
+// Store register src to [dst + offset]
+#[inline]
+fn emit_store (jit: &mut JitMemory, size: OperandSize, src: u8, dst: u8, offset: i32) {
+    match size {
+        OperandSize::S16 => emit1(jit, 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 {
+        let is_masked = | val, mask | {
+            match val & mask {
+                0 => 0,
+                _ => 1
+            }
+        };
+        emit_rex(jit, rexw, is_masked(src, 8), 0, is_masked(dst, 8));
+    }
+    match size {
+        OperandSize::S8 => emit1(jit, 0x88),
+        _               => emit1(jit, 0x89),
+    };
+    emit_modrm_and_displacement(jit, src, dst, offset);
+}
+
+// Store immediate to [dst + offset]
+#[inline]
+fn emit_store_imm32 (jit: &mut JitMemory, size: OperandSize, dst: u8, offset: i32, imm: i32) {
+    match size {
+        OperandSize::S16 => emit1(jit, 0x66), // 16-bit override
+        _ => {},
+    };
+    match size {
+        OperandSize::S64 => emit_basic_rex(jit, 1, 0, dst),
+        _                => emit_basic_rex(jit, 0, 0, dst),
+    };
+    match size {
+        OperandSize::S8 => emit1(jit, 0xc6),
+        _               => emit1(jit, 0xc7),
+    };
+    emit_modrm_and_displacement(jit, 0, dst, offset);
+    match size {
+        OperandSize::S8  => emit1(jit, imm as u8),
+        OperandSize::S16 => emit2(jit, imm as u16),
+        _                => emit4(jit, imm as u32),
+    };
+}
+
+#[inline]
+fn emit_call (jit: &mut JitMemory, target: i64) {
+    // TODO use direct call when possible
+    emit_load_imm(jit, RAX, target);
+    // callq *%rax
+    emit1(jit, 0xff);
+    emit1(jit, 0xd0);
+}
+
+fn muldivmod(jit: &mut JitMemory, pc: u16, opc: u8, src: u8, dst: u8, imm: i32) {
+    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;
+
+    if div || modrm {
+        emit_load_imm(jit, RCX, pc as i64);
+
+        // test src,src
+        if is64 {
+            emit_alu64(jit, 0x85, src, src);
+        } else {
+            emit_alu32(jit, 0x85, src, src);
+        }
+
+        // jz div_by_zero
+        emit_jcc(jit, 0x84, TARGET_PC_DIV_BY_ZERO);
+    }
+
+    if dst != RAX {
+        emit_push(jit, RAX);
+    }
+    if dst != RDX {
+        emit_push(jit, RDX);
+    }
+    if imm != 0 {
+        emit_load_imm(jit, RCX, imm as i64);
+    } else {
+        emit_mov(jit, src, RCX);
+    }
+
+    emit_mov(jit, dst, RAX);
+
+    if div || modrm {
+        // xor %edx,%edx
+        emit_alu32(jit, 0x31, RDX, RDX);
+    }
+
+    if is64 {
+        emit_rex(jit, 1, 0, 0, 0);
+    }
+
+    // mul %ecx or div %ecx
+    emit_alu32(jit, 0xf7, if mul { 4 } else { 6 }, RCX);
+
+    if dst != RDX {
+        if modrm {
+            emit_mov(jit, RDX, dst);
+        }
+        emit_pop(jit, RDX);
+    }
+    if dst != RAX {
+        if div || mul {
+            emit_mov(jit, RAX, dst);
+        }
+        emit_pop(jit, RAX);
+    }
+}
+
+#[derive(Debug)]
+struct Jump {
+    offset_loc: usize,
+    target_pc:  isize,
+}
+
+struct JitMemory<'a> {
+    contents:        &'a mut [u8],
+    offset:          usize,
+    pc_locs:         std::vec::Vec<usize>,
+    special_targets: HashMap<isize, usize>,
+    jumps:           std::vec::Vec<Jump>,
+}
+
+impl<'a> JitMemory<'a> {
+    fn new(num_pages: usize) -> JitMemory<'a> {
+        let contents: &mut[u8];
+        unsafe {
+            let size = num_pages * PAGE_SIZE;
+            let mut raw: *mut libc::c_void = mem::uninitialized();
+            libc::posix_memalign(&mut raw, PAGE_SIZE, size);
+            libc::mprotect(raw, size, libc::PROT_EXEC | libc::PROT_READ | libc::PROT_WRITE);
+            memset(raw, 0xc3, size);  // for now, prepopulate with 'RET' calls
+            contents = std::slice::from_raw_parts_mut(mem::transmute(raw), num_pages * PAGE_SIZE);
+        }
+
+        JitMemory {
+            contents:        contents,
+            offset:          0,
+            pc_locs:         vec![],
+            jumps:           vec![],
+            special_targets: HashMap::new(),
+        }
+    }
+
+    fn jit_compile(&mut self, prog: &[u8], use_mbuff: bool, update_data_ptr: bool,
+                   helpers: &HashMap<u32, fn (u64, u64, u64, u64, u64) -> u64>) {
+        emit_push(self, RBP);
+        emit_push(self, RBX);
+        emit_push(self, R13);
+        emit_push(self, R14);
+        emit_push(self, R15);
+
+        // RDI: mbuff
+        // RSI: mbuff_len
+        // RDX: mem
+        // RCX: mem_len
+        // R8:  mem_offset
+        // R9:  mem_end_offset
+        match (use_mbuff, update_data_ptr) {
+            (false, _) => {
+                // We do not use any mbuff. Move mem pointer into register 1.
+                if map_register(1) != RDX {
+                    emit_mov(self, 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 {
+                    emit_mov(self, 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.
+                emit_alu64(self, 0x01, RDI, R8);                // add mbuff to mem_offset in R8
+                emit_store(self, OperandSize::S64, RDX, R8, 0); // set mem at mbuff + mem_offset
+                // Store mem_end at mbuff + mem_end_offset. Trash R9.
+                emit_load(self, OperandSize::S64, RDX, R8, 0);  // load mem into R8
+                emit_alu64(self, 0x01, RCX, R8);                // add mem_len to mem (= mem_end)
+                emit_alu64(self, 0x01, RDI, R9);                // add mbuff to mem_end_offset
+                emit_store(self, OperandSize::S64, R8, R9, 0);  // store mem_end
+
+                // Move rdi into register 1
+                if map_register(1) != RDI {
+                    emit_mov(self, RDI, map_register(1));
+                }
+            }
+        }
+
+        // Copy stack pointer to R10
+        emit_mov(self, RSP, map_register(10));
+
+        // Allocate stack space
+        emit_alu64_imm32(self, 0x81, 5, RSP, ebpf::STACK_SIZE as i32);
+
+        self.pc_locs = vec![0; prog.len() / ebpf::INSN_SIZE + 1];
+
+        let mut insn_ptr:usize = 0;
+        while insn_ptr * ebpf::INSN_SIZE < prog.len() {
+            let insn = get_insn(prog, insn_ptr);
+
+            self.pc_locs[insn_ptr] = self.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;
+
+            match insn.opc {
+
+                // BPF_LD class
+                ebpf::LD_ABS_B   => unimplemented!(),
+                ebpf::LD_ABS_H   => unimplemented!(),
+                ebpf::LD_ABS_W   => unimplemented!(),
+                ebpf::LD_ABS_DW  => unimplemented!(),
+                ebpf::LD_IND_B   => unimplemented!(),
+                ebpf::LD_IND_H   => unimplemented!(),
+                ebpf::LD_IND_W   => unimplemented!(),
+                ebpf::LD_IND_DW  => unimplemented!(),
+
+                // BPF_LDX class
+                ebpf::LD_DW_IMM  => {
+                    insn_ptr += 1;
+                    let second_part = get_insn(prog, insn_ptr).imm as u64;
+                    let imm = (insn.imm as u32) as u64 | second_part.wrapping_shl(32);
+                    emit_load_imm(self, dst, imm as i64);
+                },
+                ebpf::LD_B_REG   =>
+                    emit_load(self, OperandSize::S8,  src, dst, insn.off as i32),
+                ebpf::LD_H_REG   =>
+                    emit_load(self, OperandSize::S16, src, dst, insn.off as i32),
+                ebpf::LD_W_REG   =>
+                    emit_load(self, OperandSize::S32, src, dst, insn.off as i32),
+                ebpf::LD_DW_REG  =>
+                    emit_load(self, OperandSize::S64, src, dst, insn.off as i32),
+
+                // BPF_ST class
+                ebpf::ST_B_IMM   =>
+                    emit_store_imm32(self, OperandSize::S8,  dst, insn.off as i32, insn.imm),
+                ebpf::ST_H_IMM   =>
+                    emit_store_imm32(self, OperandSize::S16, dst, insn.off as i32, insn.imm),
+                ebpf::ST_W_IMM   =>
+                    emit_store_imm32(self, OperandSize::S32, dst, insn.off as i32, insn.imm),
+                ebpf::ST_DW_IMM  =>
+                    emit_store_imm32(self, OperandSize::S64, dst, insn.off as i32, insn.imm),
+
+                // BPF_STX class
+                ebpf::ST_B_REG   =>
+                    emit_store(self, OperandSize::S8,  src, dst, insn.off as i32),
+                ebpf::ST_H_REG   =>
+                    emit_store(self, OperandSize::S16, src, dst, insn.off as i32),
+                ebpf::ST_W_REG   =>
+                    emit_store(self, OperandSize::S32, src, dst, insn.off as i32),
+                ebpf::ST_DW_REG  =>
+                    emit_store(self, OperandSize::S64, src, dst, insn.off as i32),
+                ebpf::ST_W_XADD  => unimplemented!(),
+                ebpf::ST_DW_XADD => unimplemented!(),
+
+                // BPF_ALU class
+                ebpf::ADD32_IMM  => emit_alu32_imm32(self, 0x81, 0, dst, insn.imm),
+                ebpf::ADD32_REG  => emit_alu32(self, 0x01, src, dst),
+                ebpf::SUB32_IMM  => emit_alu32_imm32(self, 0x81, 5, dst, insn.imm),
+                ebpf::SUB32_REG  => emit_alu32(self, 0x29, src, dst),
+                ebpf::MUL32_IMM | ebpf::MUL32_REG |
+                    ebpf::DIV32_IMM | ebpf::DIV32_REG |
+                    ebpf::MOD32_IMM | ebpf::MOD32_REG =>
+                    muldivmod(self, insn_ptr as u16, insn.opc, src, dst, insn.imm),
+                ebpf::OR32_IMM   => emit_alu32_imm32(self, 0x81, 1, dst, insn.imm),
+                ebpf::OR32_REG   => emit_alu32(self, 0x09, src, dst),
+                ebpf::AND32_IMM  => emit_alu32_imm32(self, 0x81, 4, dst, insn.imm),
+                ebpf::AND32_REG  => emit_alu32(self, 0x21, src, dst),
+                ebpf::LSH32_IMM  => emit_alu32_imm8(self, 0xc1, 4, dst, insn.imm as i8),
+                ebpf::LSH32_REG  => {
+                    emit_mov(self, src, RCX);
+                    emit_alu32(self, 0xd3, 4, dst);
+                },
+                ebpf::RSH32_IMM  => emit_alu32_imm8(self, 0xc1, 5, dst, insn.imm as i8),
+                ebpf::RSH32_REG  => {
+                    emit_mov(self, src, RCX);
+                    emit_alu32(self, 0xd3, 5, dst);
+                },
+                ebpf::NEG32      => emit_alu32(self, 0xf7, 3, dst),
+                ebpf::XOR32_IMM  => emit_alu32_imm32(self, 0x81, 6, dst, insn.imm),
+                ebpf::XOR32_REG  => emit_alu32(self, 0x31, src, dst),
+                ebpf::MOV32_IMM  => emit_alu32_imm32(self, 0xc7, 0, dst, insn.imm),
+                ebpf::MOV32_REG  => emit_mov(self, src, dst),
+                ebpf::ARSH32_IMM => emit_alu32_imm8(self, 0xc1, 7, dst, insn.imm as i8),
+                ebpf::ARSH32_REG => {
+                    emit_mov(self, src, RCX);
+                    emit_alu32(self, 0xd3, 7, dst);
+                },
+                ebpf::LE         => {}, // No-op
+                ebpf::BE         => {
+                    match insn.imm {
+                        16 => {
+                            // rol
+                            emit1(self, 0x66); // 16-bit override
+                            emit_alu32_imm8(self, 0xc1, 0, dst, 8);
+                            // and
+                            emit_alu32_imm32(self, 0x81, 4, dst, 0xffff);
+                        }
+                        32 | 64 => {
+                            // bswap
+                            let bit = match insn.imm { 64 => 1, _ => 0 };
+                            emit_basic_rex(self, bit, 0, dst);
+                            emit1(self, 0x0f);
+                            emit1(self, 0xc8 | (dst & 0b111));
+                        }
+                        _ => unreachable!() // Should have been caught by verifier
+                    }
+                },
+
+                // BPF_ALU64 class
+                ebpf::ADD64_IMM  => emit_alu64_imm32(self, 0x81, 0, dst, insn.imm),
+                ebpf::ADD64_REG  => emit_alu64(self, 0x01, src, dst),
+                ebpf::SUB64_IMM  => emit_alu64_imm32(self, 0x81, 5, dst, insn.imm),
+                ebpf::SUB64_REG  => emit_alu64(self, 0x29, src, dst),
+                ebpf::MUL64_IMM | ebpf::MUL64_REG |
+                    ebpf::DIV64_IMM | ebpf::DIV64_REG |
+                    ebpf::MOD64_IMM | ebpf::MOD64_REG  =>
+                    muldivmod(self, insn_ptr as u16, insn.opc, src, dst, insn.imm),
+                ebpf::OR64_IMM   => emit_alu64_imm32(self, 0x81, 1, dst, insn.imm),
+                ebpf::OR64_REG   => emit_alu64(self, 0x09, src, dst),
+                ebpf::AND64_IMM  => emit_alu64_imm32(self, 0x81, 4, dst, insn.imm),
+                ebpf::AND64_REG  => emit_alu64(self, 0x21, src, dst),
+                ebpf::LSH64_IMM  => emit_alu64_imm8(self, 0xc1, 4, dst, insn.imm as i8),
+                ebpf::LSH64_REG  => {
+                    emit_mov(self, src, RCX);
+                    emit_alu64(self, 0xd3, 4, dst);
+                },
+                ebpf::RSH64_IMM  =>  emit_alu64_imm8(self, 0xc1, 5, dst, insn.imm as i8),
+                ebpf::RSH64_REG  => {
+                    emit_mov(self, src, RCX);
+                    emit_alu64(self, 0xd3, 5, dst);
+                },
+                ebpf::NEG64      => emit_alu64(self, 0xf7, 3, dst),
+                ebpf::XOR64_IMM  => emit_alu64_imm32(self, 0x81, 6, dst, insn.imm),
+                ebpf::XOR64_REG  => emit_alu64(self, 0x31, src, dst),
+                ebpf::MOV64_IMM  => emit_load_imm(self, dst, insn.imm as i64),
+                ebpf::MOV64_REG  => emit_mov(self, src, dst),
+                ebpf::ARSH64_IMM => emit_alu64_imm8(self, 0xc1, 7, dst, insn.imm as i8),
+                ebpf::ARSH64_REG => {
+                    emit_mov(self, src, RCX);
+                    emit_alu64(self, 0xd3, 7, dst);
+                },
+
+                // BPF_JMP class
+                ebpf::JA         => emit_jmp(self, target_pc),
+                ebpf::JEQ_IMM    => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x84, target_pc);
+                },
+                ebpf::JEQ_REG    => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x84, target_pc);
+                },
+                ebpf::JGT_IMM    => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x87, target_pc);
+                },
+                ebpf::JGT_REG    => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x87, target_pc);
+                },
+                ebpf::JGE_IMM    => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x83, target_pc);
+                },
+                ebpf::JGE_REG    => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x83, target_pc);
+                },
+                ebpf::JSET_IMM   => {
+                    emit_alu64_imm32(self, 0xf7, 0, dst, insn.imm);
+                    emit_jcc(self, 0x85, target_pc);
+                },
+                ebpf::JSET_REG   => {
+                    emit_alu64(self, 0x85, src, dst);
+                    emit_jcc(self, 0x85, target_pc);
+                },
+                ebpf::JNE_IMM    => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x85, target_pc);
+                },
+                ebpf::JNE_REG    => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x85, target_pc);
+                },
+                ebpf::JSGT_IMM   => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x8f, target_pc);
+                },
+                ebpf::JSGT_REG   => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x8f, target_pc);
+                },
+                ebpf::JSGE_IMM   => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x8d, target_pc);
+                },
+                ebpf::JSGE_REG   => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x8d, target_pc);
+                },
+                ebpf::CALL       => {
+                    // 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(_) = helpers.get(&(insn.imm as u32)) {
+                        // We reserve RCX for shifts
+                        emit_mov(self, R9, RCX);
+                        emit_call(self, helpers[&(insn.imm as u32)] as i64);
+                    } else {
+                        panic!("[JIT] Error: unknown helper function (id: {:#x})",
+                               insn.imm as u32);
+                    };
+                },
+                ebpf::TAIL_CALL  => { unimplemented!() },
+                ebpf::EXIT       => {
+                    if insn_ptr != prog.len() / ebpf::INSN_SIZE - 1 {
+                        emit_jmp(self, TARGET_PC_EXIT);
+                    };
+                },
+
+                _                => {
+                    panic!("[JIT] Error: unknown eBPF opcode {:#2x} (insn #{:?})",
+                           insn.opc, insn_ptr);
+                },
+            }
+
+            insn_ptr += 1;
+        }
+
+        // Epilogue
+        set_anchor(self, TARGET_PC_EXIT);
+
+        // Move register 0 into rax
+        if map_register(0) != RAX {
+            emit_mov(self, map_register(0), RAX);
+        }
+
+        // Deallocate stack space
+        emit_alu64_imm32(self, 0x81, 0, RSP, ebpf::STACK_SIZE as i32);
+
+        emit_pop(self, R15);
+        emit_pop(self, R14);
+        emit_pop(self, R13);
+        emit_pop(self, RBX);
+        emit_pop(self, RBP);
+
+        emit1(self, 0xc3); // ret
+
+        // Division by zero handler
+        set_anchor(self, TARGET_PC_DIV_BY_ZERO);
+        fn log (pc: u64) -> i64 {
+            // Write error message on stderr.
+            // We would like to panic!() instead (but does not work here), or maybe return an
+            // error, that is, if we also turn all other panics into errors someday.
+            // Note: needs `use std::io::Write;`
+            //     let res = writeln!(&mut std::io::stderr(),
+            //                        "[JIT] Error: division by zero (insn {:?})\n", pc);
+            //     match res {
+            //         Ok(_)  => 0,
+            //         Err(_) => -1
+            //     }
+            pc as i64 // Just to prevent warnings
+        };
+        emit_mov(self, RCX, RDI); // muldivmod stored pc in RCX
+        emit_call(self, log as i64);
+        emit_load_imm(self, map_register(0), -1);
+        emit_jmp(self, TARGET_PC_EXIT);
+    }
+
+    fn resolve_jumps(&mut self)
+    {
+        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
+            unsafe {
+                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 = self.contents.as_ptr().offset(jump.offset_loc as isize);
+
+                libc::memcpy(offset_ptr as *mut libc::c_void, rel as *const libc::c_void,
+                             std::mem::size_of::<i32>());
+            }
+        }
+    }
+} // struct JitMemory
+
+impl<'a> Index<usize> for JitMemory<'a> {
+    type Output = u8;
+
+    fn index(&self, _index: usize) -> &u8 {
+        //unsafe {&*self.contents.offset(_index as isize) }
+        &self.contents[_index]
+    }
+}
+
+impl<'a> IndexMut<usize> for JitMemory<'a> {
+    fn index_mut(&mut self, _index: usize) -> &mut u8 {
+        //unsafe {&mut *self.contents.offset(_index as isize) }
+        &mut self.contents[_index]
+    }
+}
+
+// TODO: get rid of one of the two get_insn. They're the same except one takes vector, while this
+// one takes a slice. Can we use slices everywhere for the programs?
+fn get_insn(prog: &[u8], idx: usize) -> ebpf::Insn {
+    let insn = ebpf::Insn {
+        opc:  prog[ebpf::INSN_SIZE * idx],
+        dst:  prog[ebpf::INSN_SIZE * idx + 1] & 0x0f,
+        src: (prog[ebpf::INSN_SIZE * idx + 1] & 0xf0) >> 4,
+        off: unsafe { let x = prog.as_ptr().offset((ebpf::INSN_SIZE * idx + 2) as isize) as *const i16; *x },
+        imm: unsafe { let x = prog.as_ptr().offset((ebpf::INSN_SIZE * idx + 4) as isize) as *const i32; *x },
+    };
+    insn
+}
+
+impl<'a> std::fmt::Debug for JitMemory<'a> {
+    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
+        fmt.write_str("JIT contents: [")?;
+        let mut j = 0;// XXX XXX
+        for i in self.contents as &[u8] {
+            fmt.write_fmt(format_args!(" {:#04x},", i))?;
+            j += 1; if j > 100 { break; }
+        };
+        fmt.write_str(" ] | ")?;
+        fmt.debug_struct("JIT state")
+            .field("offset", &self.offset)
+            .field("pc_locs", &self.pc_locs)
+            .field("special_targets", &self.special_targets)
+            .field("jumps", &self.jumps)
+            .finish()
+    }
+}
+
+// In the end, this is the only thing we export
+pub fn compile(prog: &std::vec::Vec<u8>,
+               helpers: &HashMap<u32, fn (u64, u64, u64, u64, u64) -> u64>,
+               use_mbuff: bool, update_data_ptr: bool)
+    -> (fn(*mut u8, usize, *mut u8, usize, usize, usize) -> u64) {
+
+    let mut jit = JitMemory::new(1);
+    jit.jit_compile(prog, use_mbuff, update_data_ptr, helpers);
+    jit.resolve_jumps();
+
+    unsafe { mem::transmute(jit.contents.as_ptr()) }
+}

+ 533 - 0
src/lib.rs

@@ -0,0 +1,533 @@
+// Derived from uBPF <https://github.com/iovisor/ubpf>
+// Copyright 2015 Big Switch Networks, Inc
+//      (uBPF: VM architecture, parts of the interpreter, originally in C)
+// Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com>
+//      (Translation to Rust, MetaBuff/multiple classes addition, hashmaps for helpers)
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+// One day we'll uncomment this!
+// #![warn(missing_docs)]
+
+use std::u32;
+use std::collections::HashMap;
+
+extern crate libc;
+
+pub mod ebpf;
+pub mod helpers;
+mod verifier;
+mod jit;
+
+struct MetaBuff {
+    data_offset:     usize,
+    data_end_offset: usize,
+    buffer:          std::vec::Vec<u8>,
+}
+
+struct EbpfVm<'a> {
+    prog:    &'a std::vec::Vec<u8>,
+    jit:     (fn (*mut u8, usize, *mut u8, usize, usize, usize) -> u64),
+    helpers: HashMap<u32, fn (u64, u64, u64, u64, u64) -> u64>,
+}
+
+// Runs on packet + metadata buffer
+impl<'a> EbpfVm<'a> {
+
+    fn new(prog: &'a std::vec::Vec<u8>) -> EbpfVm<'a> {
+        verifier::check(prog);
+
+        #[allow(unused_variables)]
+        fn no_jit(foo: *mut u8, foo_len: usize, bar: *mut u8, bar_len: usize,
+                  nodata_offset: usize, nodata_end_offset: usize) -> u64 {
+            panic!("Error: program has not been JIT-compiled");
+        }
+
+        EbpfVm {
+            prog:    prog,
+            jit:     no_jit,
+            helpers: HashMap::new(),
+        }
+    }
+
+    fn set_prog(&mut self, prog: &'a std::vec::Vec<u8>) {
+        verifier::check(prog);
+        self.prog = prog;
+    }
+
+    fn register_helper(&mut self, key: u32, function: fn (u64, u64, u64, u64, u64) -> u64) {
+        self.helpers.insert(key, function);
+    }
+
+    fn prog_exec(&self, mem: &mut std::vec::Vec<u8>, mbuff: &'a mut MetaBuff) -> u64 {
+        const U32MAX: u64 = u32::MAX as u64;
+
+        let stack = vec![0u8;ebpf::STACK_SIZE];
+
+        // R1 points to beginning of memory area, R10 to stack
+        let mut reg: [u64;11] = [
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, stack.as_ptr() as u64 + stack.len() as u64
+        ];
+        if mbuff.buffer.len() > 0 {
+            reg[1] = mbuff.buffer.as_ptr() as u64;
+        }
+        else if mem.len() > 0 {
+            reg[1] = mem.as_ptr() as u64;
+        }
+
+        let check_mem_load = | addr: u64, len: usize, insn_ptr: usize | {
+            EbpfVm::check_mem(addr, len, "load", insn_ptr, &mbuff.buffer, &mem, &stack);
+        };
+        let check_mem_store = | addr: u64, len: usize, insn_ptr: usize | {
+            EbpfVm::check_mem(addr, len, "store", insn_ptr, &mbuff.buffer, &mem, &stack);
+        };
+
+        // Loop on instructions
+        let mut insn_ptr:usize = 0;
+        while insn_ptr * ebpf::INSN_SIZE < self.prog.len() {
+            let insn = ebpf::get_insn(self.prog, insn_ptr);
+            insn_ptr += 1;
+            // println!("R0: {:#x} R1: {:#x} R2: {:#x} R3: {:#x} R4: {:#x} R5: {:#x} R6: {:#x} R7: {:#x} R8: {:#x} R9: {:#x} R10: {:#x}",
+            //          reg[0], reg[1], reg[2], reg[3], reg[4], reg[5], reg[6], reg[7], reg[8], reg[9], reg[10]);
+            // println!("{:02x} {:x} {:x} {:04x} {:08x}", insn.opc, insn.dst, insn.src, insn.off, insn.imm);
+            let _dst    = insn.dst as usize;
+            let _src    = insn.src as usize;
+
+            match insn.opc {
+
+                // BPF_LD class
+                ebpf::LD_ABS_B   => unimplemented!(),
+                ebpf::LD_ABS_H   => unimplemented!(),
+                ebpf::LD_ABS_W   => unimplemented!(),
+                ebpf::LD_ABS_DW  => unimplemented!(),
+                ebpf::LD_IND_B   => unimplemented!(),
+                ebpf::LD_IND_H   => unimplemented!(),
+                ebpf::LD_IND_W   => unimplemented!(),
+                ebpf::LD_IND_DW  => unimplemented!(),
+
+                // BPF_LDX class
+                ebpf::LD_DW_IMM  => {
+                    let next_insn = ebpf::get_insn(self.prog, insn_ptr);
+                    insn_ptr += 1;
+                    reg[_dst] = ((insn.imm as u32) as u64) + ((next_insn.imm as u64) << 32);
+                },
+                ebpf::LD_B_REG   => reg[_dst] = unsafe {
+                    let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u8;
+                    check_mem_load(x as u64, 1, insn_ptr);
+                    *x as u64
+                },
+                ebpf::LD_H_REG   => reg[_dst] = unsafe {
+                    let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u16;
+                    check_mem_load(x as u64, 2, insn_ptr);
+                    *x as u64
+                },
+                ebpf::LD_W_REG   => reg[_dst] = unsafe {
+                    let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u32;
+                    check_mem_load(x as u64, 4, insn_ptr);
+                    *x as u64
+                },
+                ebpf::LD_DW_REG  => reg[_dst] = unsafe {
+                    let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u64;
+                    check_mem_load(x as u64, 8, insn_ptr);
+                    *x as u64
+                },
+
+                // BPF_ST class
+                ebpf::ST_B_IMM   => unsafe {
+                    let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u8;
+                    check_mem_store(x as u64, 1, insn_ptr);
+                    *x = 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(x as u64, 2, insn_ptr);
+                    *x = 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(x as u64, 4, insn_ptr);
+                    *x = 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(x as u64, 8, insn_ptr);
+                    *x = insn.imm as u64;
+                },
+
+                // BPF_STX class
+                ebpf::ST_B_REG   => unsafe {
+                    let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u8;
+                    check_mem_store(x as u64, 1, insn_ptr);
+                    *x = reg[_src] as u8;
+                },
+                ebpf::ST_H_REG   => unsafe {
+                    let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u16;
+                    check_mem_store(x as u64, 2, insn_ptr);
+                    *x = reg[_src] as u16;
+                },
+                ebpf::ST_W_REG   => unsafe {
+                    let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u32;
+                    check_mem_store(x as u64, 4, insn_ptr);
+                    *x = reg[_src] as u32;
+                },
+                ebpf::ST_DW_REG  => unsafe {
+                    let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u64;
+                    check_mem_store(x as u64, 8, insn_ptr);
+                    *x = reg[_src] as u64;
+                },
+                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  => 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  => reg[_dst] = (reg[_dst] as u32 / insn.imm              as u32) as u64,
+                ebpf::DIV32_REG  => {
+                    if reg[_src] == 0 {
+                        panic!("Error: division by 0");
+                    }
+                    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,
+                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,
+                ebpf::NEG32      => { reg[_dst] = (reg[_dst] as i32).wrapping_neg()                 as u64; reg[_dst] &= U32MAX; },
+                ebpf::MOD32_IMM  =>   reg[_dst] = (reg[_dst] as u32             % insn.imm  as u32) as u64,
+                ebpf::MOD32_REG  => {
+                    if reg[_src] == 0 {
+                        panic!("Error: division by 0");
+                    }
+                    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 u64,
+                ebpf::MOV32_REG  =>   reg[_dst] = (reg[_src] as u32)                                as u64,
+                ebpf::ARSH32_IMM => { reg[_dst] = (reg[_dst] as i32).wrapping_shr(insn.imm  as u32) as u64; reg[_dst] &= U32MAX; },
+                ebpf::ARSH32_REG => { reg[_dst] = (reg[_dst] as i32).wrapping_shr(reg[_src] as u32) as u64; reg[_dst] &= U32MAX; },
+                ebpf::LE         => {
+                    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(),
+                        _  => unreachable!(),
+                    };
+                },
+                ebpf::BE         => {
+                    reg[_dst] = match insn.imm {
+                        16 => (reg[_dst] as u16).to_be() as u64,
+                        32 => (reg[_dst] as u32).to_be() as u64,
+                        64 =>  reg[_dst].to_be(),
+                        _  => unreachable!(),
+                    };
+                },
+
+                // BPF_ALU64 class
+                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  => reg[_dst]                       /= insn.imm as u64,
+                ebpf::DIV64_REG  => {
+                    if reg[_src] == 0 {
+                        panic!("Error: division by 0");
+                    }
+                    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,
+                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::MOD64_IMM  => reg[_dst] %=  insn.imm as u64,
+                ebpf::MOD64_REG  => {
+                    if reg[_src] == 0 {
+                        panic!("Error: division by 0");
+                    }
+                    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],
+                ebpf::ARSH64_IMM => reg[_dst] = (reg[_dst] as i64 >> insn.imm as u64) as u64,
+                ebpf::ARSH64_REG => reg[_dst] = (reg[_dst] as i64 >> reg[_src])       as u64,
+
+                // BPF_JMP class
+                // TODO: check this actually works as expected for signed / unsigned ops
+                ebpf::JA         =>                                           insn_ptr = (insn_ptr as i16 + insn.off) as usize,
+                ebpf::JEQ_IMM    => if reg[_dst] == insn.imm as u64         { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JEQ_REG    => if reg[_dst] == reg[_src]               { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JGT_IMM    => if reg[_dst] >  insn.imm as u64         { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JGT_REG    => if reg[_dst] >  reg[_src]               { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JGE_IMM    => if reg[_dst] >= insn.imm as u64         { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JGE_REG    => if reg[_dst] >= reg[_src]               { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSET_IMM   => if reg[_dst] &  insn.imm as u64 != 0    { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSET_REG   => if reg[_dst] &  reg[_src]       != 0    { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JNE_IMM    => if reg[_dst] != insn.imm as u64         { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JNE_REG    => if reg[_dst] != reg[_src]               { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSGT_IMM   => if reg[_dst] as i64 >  insn.imm  as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSGT_REG   => if reg[_dst] as i64 >  reg[_src] as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSGE_IMM   => if reg[_dst] as i64 >= insn.imm  as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSGE_REG   => if reg[_dst] as i64 >= reg[_src] as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                // Do not delegate the check to the verifier, since registered functions can be
+                // changed after the program has been verified.
+                ebpf::CALL       => if let Some(function) = self.helpers.get(&(insn.imm as u32)) {
+                    reg[0] = function(reg[1], reg[2], reg[3], reg[4], reg[5]);
+                } else {
+                    panic!("Error: unknown helper function (id: {:#x})", insn.imm as u32);
+                },
+                ebpf::TAIL_CALL  => unimplemented!(),
+                ebpf::EXIT       => return reg[0],
+
+                _                => unreachable!()
+            }
+        }
+
+        return 0;
+    }
+
+    fn check_mem(addr: u64, len: usize, access_type: &str, insn_ptr: usize,
+                 mbuff: &std::vec::Vec<u8>, mem: &std::vec::Vec<u8>, stack: &std::vec::Vec<u8>) {
+        // WARNING: untested
+        if mbuff.as_ptr() as u64 <= addr && addr + len as u64 <= mbuff.as_ptr() as u64 + mbuff.len() as u64 {
+            return
+        }
+        if mem.as_ptr() as u64 <= addr && addr + len as u64 <= mem.as_ptr() as u64 + mem.len() as u64 {
+            return
+        }
+        if stack.as_ptr() as u64 <= addr && addr + len as u64 <= stack.as_ptr() as u64 + stack.len() as u64 {
+            return
+        }
+
+        panic!(
+            "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()
+        );
+    }
+
+    // Not used by “child” structs. Make it a trait?
+    // fn jit_compile(&mut self) {
+    // }
+
+    fn prog_exec_jit(&self, mem: &mut std::vec::Vec<u8>, mbuff: &'a mut MetaBuff) -> u64 {
+        // If packet data is empty, do not send the address of an empty vector; send a null
+        // pointer (zero value) 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 => 0 as *mut u8,
+            _ => mem.as_ptr() as *mut u8
+        };
+        (self.jit)(mbuff.buffer.as_ptr() as *mut u8, mbuff.buffer.len(), mem_ptr, mem.len(),
+                   mbuff.data_offset, mbuff.data_end_offset)
+    }
+}
+
+// Runs on packet data, with a metadata buffer
+pub struct EbpfVmMbuff<'a> {
+    parent: EbpfVm<'a>,
+}
+
+impl<'a> EbpfVmMbuff<'a> {
+
+    pub fn new(prog: &'a std::vec::Vec<u8>) -> EbpfVmMbuff<'a> {
+        let parent = EbpfVm::new(prog);
+        EbpfVmMbuff {
+            parent: parent,
+        }
+    }
+
+    pub fn set_prog(&mut self, prog: &'a std::vec::Vec<u8>) {
+        self.parent.set_prog(prog)
+    }
+
+    pub fn register_helper(&mut self, key: u32, function: fn (u64, u64, u64, u64, u64) -> u64) {
+        self.parent.register_helper(key, function);
+    }
+
+    pub fn prog_exec(&self, mem: &'a mut std::vec::Vec<u8>, metadata: std::vec::Vec<u8>) -> u64 {
+        let mut mbuff = MetaBuff {
+            data_offset:     0,
+            data_end_offset: 0,
+            buffer:          metadata,
+        };
+        self.parent.prog_exec(mem, &mut mbuff)
+    }
+
+    pub fn jit_compile(&mut self) {
+        self.parent.jit = jit::compile(&self.parent.prog, &self.parent.helpers, true, false);
+    }
+
+    pub fn prog_exec_jit(&self, mem: &'a mut std::vec::Vec<u8>, metadata: std::vec::Vec<u8>) -> u64 {
+        let mut mbuff = MetaBuff {
+            data_offset:     0,
+            data_end_offset: 0,
+            buffer:          metadata,
+        };
+        self.parent.prog_exec_jit(mem, &mut mbuff)
+    }
+}
+
+// Runs on packet data, simulates a metadata buffer
+pub struct EbpfVmFixedMbuff<'a> {
+    parent: EbpfVm<'a>,
+    mbuff:  MetaBuff,
+}
+
+impl<'a> EbpfVmFixedMbuff<'a> {
+
+    pub fn new(prog: &'a std::vec::Vec<u8>, data_offset: usize, data_end_offset: usize) -> EbpfVmFixedMbuff<'a> {
+        let parent = EbpfVm::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_offset,
+            data_end_offset: data_end_offset,
+            buffer:          buffer,
+        };
+        EbpfVmFixedMbuff {
+            parent: parent,
+            mbuff:  mbuff,
+        }
+    }
+
+    pub fn set_prog(&mut self, prog: &'a std::vec::Vec<u8>) {
+        self.parent.set_prog(prog)
+    }
+
+    pub fn register_helper(&mut self, key: u32, function: fn (u64, u64, u64, u64, u64) -> u64) {
+        self.parent.register_helper(key, function);
+    }
+
+    pub fn prog_exec(&mut self, mem: &'a mut std::vec::Vec<u8>) -> u64 {
+        let l = self.mbuff.buffer.len();
+        // Can this happen? Yes, since MetaBuff is public.
+        if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l {
+            panic!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}",
+            l, self.mbuff.data_offset, self.mbuff.data_end_offset);
+        }
+        unsafe {
+            let mut data     = self.mbuff.buffer.as_ptr().offset(self.mbuff.data_offset as isize)     as *mut u64;
+            let mut data_end = self.mbuff.buffer.as_ptr().offset(self.mbuff.data_end_offset as isize) as *mut u64;
+            *data     = mem.as_ptr() as u64;
+            *data_end = mem.as_ptr() as u64 + mem.len() as u64;
+        }
+        self.parent.prog_exec(mem, &mut self.mbuff)
+    }
+
+    pub fn jit_compile(&mut self) {
+        self.parent.jit = jit::compile(&self.parent.prog, &self.parent.helpers, true, true);
+    }
+
+    pub fn prog_exec_jit(&mut self, mem: &'a mut std::vec::Vec<u8>) -> u64 {
+        self.parent.prog_exec_jit(mem, &mut self.mbuff)
+    }
+}
+
+// Runs on a packet, no metadata buffer
+pub struct EbpfVmRaw<'a> {
+    parent: EbpfVm<'a>,
+}
+
+impl<'a> EbpfVmRaw<'a> {
+
+    pub fn new(prog: &'a std::vec::Vec<u8>) -> EbpfVmRaw<'a> {
+        let parent = EbpfVm::new(prog);
+        EbpfVmRaw {
+            parent: parent,
+        }
+    }
+
+    pub fn set_prog(&mut self, prog: &'a std::vec::Vec<u8>) {
+        self.parent.set_prog(prog)
+    }
+
+    pub fn register_helper(&mut self, key: u32, function: fn (u64, u64, u64, u64, u64) -> u64) {
+        self.parent.register_helper(key, function);
+    }
+
+    pub fn prog_exec(&self, mem: &'a mut std::vec::Vec<u8>) -> u64 {
+        let mut mbuff = MetaBuff {
+            data_offset:     0,
+            data_end_offset: 0,
+            buffer:          vec![]
+        };
+        self.parent.prog_exec(mem, &mut mbuff)
+    }
+
+    pub fn jit_compile(&mut self) {
+        self.parent.jit = jit::compile(&self.parent.prog, &self.parent.helpers, false, false);
+    }
+
+    pub fn prog_exec_jit(&self, mem: &'a mut std::vec::Vec<u8>) -> u64 {
+        let mut mbuff = MetaBuff {
+            data_offset:     0,
+            data_end_offset: 0,
+            buffer:          vec![]
+        };
+        //println!("{:?}", &mbuff.buffer);
+        //println!("{:?}", &mem);
+        //println!("{:?}", mem.as_ptr() as *const u64);
+        self.parent.prog_exec_jit(mem, &mut mbuff)
+    }
+}
+
+// Runs without data -- no packet, no metadata buffer
+pub struct EbpfVmNoData<'a> {
+    parent: EbpfVmRaw<'a>,
+}
+
+impl<'a> EbpfVmNoData<'a> {
+
+    pub fn new(prog: &'a std::vec::Vec<u8>) -> EbpfVmNoData<'a> {
+        let parent = EbpfVmRaw::new(prog);
+        EbpfVmNoData {
+            parent: parent,
+        }
+    }
+
+    pub fn set_prog(&mut self, prog: &'a std::vec::Vec<u8>) {
+        self.parent.set_prog(prog)
+    }
+
+    pub fn register_helper(&mut self, key: u32, function: fn (u64, u64, u64, u64, u64) -> u64) {
+        self.parent.register_helper(key, function);
+    }
+
+    pub fn jit_compile(&mut self) {
+        self.parent.jit_compile();
+    }
+
+    pub fn prog_exec(&self) -> u64 {
+        self.parent.prog_exec(&mut vec![])
+    }
+
+    pub fn prog_exec_jit(&self) -> u64 {
+        self.parent.prog_exec_jit(&mut vec![])
+    }
+}

+ 243 - 0
src/verifier.rs

@@ -0,0 +1,243 @@
+// Derived from uBPF <https://github.com/iovisor/ubpf>
+// Copyright 2015 Big Switch Networks, Inc
+//      (uBPF: safety checks, originally in C)
+// Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com>
+//      (Translation to Rust)
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+// 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 ebpf;
+use std;
+
+fn check_prog_len(prog: &std::vec::Vec<u8>) {
+    if prog.len() % ebpf::INSN_SIZE != 0 {
+        panic!("[Verifier] Error: eBPF program length must be a multiple of {:?} octets",
+               ebpf::INSN_SIZE);
+    }
+    if prog.len() > ebpf::PROG_MAX_SIZE {
+        panic!("[Verifier] Error: eBPF program length limited to {:?}, here {:?}",
+               ebpf::PROG_MAX_INSNS, prog.len() / ebpf::INSN_SIZE);
+    }
+
+    if prog.len() == 0 {
+        panic!("[Verifier] Error: program does not end with “EXIT” instruction");
+    }
+    let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1);
+    if last_insn.opc != ebpf::EXIT {
+        panic!("[Verifier] Error: program does not end with “EXIT” instruction");
+    }
+}
+
+fn check_imm_nonzero(insn: &ebpf::Insn, insn_ptr: usize) {
+    if insn.imm == 0 {
+        panic!("[Verifier] Error: division by 0 (insn #{:?})", insn_ptr);
+    }
+}
+
+fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) {
+    match insn.imm {
+        16 | 32 | 64 => return,
+        _ => panic!("[Verifier] Error: unsupported argument for LE/BE (insn #{:?})", insn_ptr)
+    }
+}
+
+fn check_load_dw(prog: &std::vec::Vec<u8>, insn_ptr: usize) {
+    // 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 {
+        panic!("[Verifier] Error: incomplete LD_DW instruction (insn #{:?})", insn_ptr);
+    }
+
+}
+
+fn check_jmp_offset(prog: &std::vec::Vec<u8>, insn_ptr: usize) {
+    let insn = ebpf::get_insn(prog, insn_ptr);
+    if insn.off == -1 {
+        panic!("[Verifier] Error: 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) {
+        panic!("[Verifier] Error: jump out of code to #{:?} (insn #{:?})",
+               dst_insn_ptr, insn_ptr);
+    }
+
+    let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize);
+    if dst_insn.opc == 0 {
+        panic!("[Verifier] Error: jump to middle of LD_DW at #{:?} (insn #{:?})",
+               dst_insn_ptr, insn_ptr);
+    }
+}
+
+fn check_registers(insn: &ebpf::Insn, store: bool, insn_ptr: usize) {
+    if insn.src > 10 {
+        panic!("[Verifier] Error: invalid source register (insn #{:?})", insn_ptr);
+    }
+
+    match (insn.dst, store) {
+        (0 ... 9, _) => {},
+        (10, true)   => {},
+        (10, false)  => panic!("[Verifier] Error: cannot write into register r10 (insn #{:?})",
+                               insn_ptr),
+        (_, _)       => panic!("[Verifier] Error: invalid destination register (insn #{:?})",
+                               insn_ptr)
+    }
+}
+
+pub fn check(prog: &std::vec::Vec<u8>) -> bool {
+    check_prog_len(prog);
+
+    let mut insn_ptr:usize = 0;
+    while insn_ptr * ebpf::INSN_SIZE < prog.len() {
+        let insn = ebpf::get_insn(prog, insn_ptr);
+        let mut store = false;
+
+        match insn.opc {
+
+            // BPF_LD class
+            ebpf::LD_ABS_B   => { unimplemented!(); },
+            ebpf::LD_ABS_H   => { unimplemented!(); },
+            ebpf::LD_ABS_W   => { unimplemented!(); },
+            ebpf::LD_ABS_DW  => { unimplemented!(); },
+            ebpf::LD_IND_B   => { unimplemented!(); },
+            ebpf::LD_IND_H   => { unimplemented!(); },
+            ebpf::LD_IND_W   => { unimplemented!(); },
+            ebpf::LD_IND_DW  => { unimplemented!(); },
+
+            // BPF_LDX class
+            ebpf::LD_DW_IMM  => {
+                store = true;
+                check_load_dw(prog, insn_ptr);
+                insn_ptr += 1;
+            },
+            ebpf::LD_B_REG   => {},
+            ebpf::LD_H_REG   => {},
+            ebpf::LD_W_REG   => {},
+            ebpf::LD_DW_REG  => {},
+
+            // BPF_ST class
+            ebpf::ST_B_IMM   => store = true,
+            ebpf::ST_H_IMM   => store = true,
+            ebpf::ST_W_IMM   => store = true,
+            ebpf::ST_DW_IMM  => store = true,
+
+            // BPF_STX class
+            ebpf::ST_B_REG   => store = true,
+            ebpf::ST_H_REG   => store = true,
+            ebpf::ST_W_REG   => store = true,
+            ebpf::ST_DW_REG  => store = true,
+            ebpf::ST_W_XADD  => { unimplemented!(); },
+            ebpf::ST_DW_XADD => { unimplemented!(); },
+
+            // BPF_ALU class
+            ebpf::ADD32_IMM  => {},
+            ebpf::ADD32_REG  => {},
+            ebpf::SUB32_IMM  => {},
+            ebpf::SUB32_REG  => {},
+            ebpf::MUL32_IMM  => {},
+            ebpf::MUL32_REG  => {},
+            ebpf::DIV32_IMM  => { check_imm_nonzero(&insn, insn_ptr); },
+            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  => { check_imm_nonzero(&insn, insn_ptr); },
+            ebpf::MOD32_REG  => {},
+            ebpf::XOR32_IMM  => {},
+            ebpf::XOR32_REG  => {},
+            ebpf::MOV32_IMM  => {},
+            ebpf::MOV32_REG  => {},
+            ebpf::ARSH32_IMM => {},
+            ebpf::ARSH32_REG => {},
+            ebpf::LE         => { check_imm_endian(&insn, insn_ptr); },
+            ebpf::BE         => { check_imm_endian(&insn, insn_ptr); },
+
+            // BPF_ALU64 class
+            ebpf::ADD64_IMM  => {},
+            ebpf::ADD64_REG  => {},
+            ebpf::SUB64_IMM  => {},
+            ebpf::SUB64_REG  => {},
+            ebpf::MUL64_IMM  => { check_imm_nonzero(&insn, insn_ptr); },
+            ebpf::MUL64_REG  => {},
+            ebpf::DIV64_IMM  => { check_imm_nonzero(&insn, insn_ptr); },
+            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 => {},
+
+            // BPF_JMP class
+            ebpf::JA         => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JEQ_IMM    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JEQ_REG    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JGT_IMM    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JGT_REG    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JGE_IMM    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JGE_REG    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSET_IMM   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSET_REG   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JNE_IMM    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JNE_REG    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSGT_IMM   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSGT_REG   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSGE_IMM   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSGE_REG   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::CALL       => {},
+            ebpf::TAIL_CALL  => { unimplemented!() },
+            ebpf::EXIT       => {},
+
+            _                => {
+                panic!("[Verifier] Error: unknown eBPF opcode {:#2x} (insn #{:?})",
+                       insn.opc, insn_ptr);
+            },
+        }
+
+        check_registers(&insn, store, insn_ptr);
+
+        insn_ptr += 1;
+    }
+
+    // insn_ptr should now be equal to number of instructions.
+    if insn_ptr != prog.len() / ebpf::INSN_SIZE {
+        panic!("[Verifier] Error: jumped out of code to #{:?}", insn_ptr);
+    }
+
+    true
+}

+ 305 - 0
tests/misc.rs

@@ -0,0 +1,305 @@
+// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+// These crates would be needed to load bytecode from a BPF-compiled object file. Since the crates
+// are not used anywhere else in the library, it is deactivated: we do not want to load and compile
+// them just for the tests. If you want to use them, do not forget to add the following
+// dependencies to your Cargo.toml file:
+//
+// ---
+// byteorder = "0.5.3"
+// elf = "0.0.10"
+// ---
+//
+// extern crate byteorder;
+// extern crate elf;
+// use std::path::PathBuf;
+
+extern crate rbpf;
+use rbpf::helpers;
+
+// The following two examples have been compiled from C with the following command:
+//
+// ```bash
+//  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)
+//         return 0;
+//     if (iph->protocol != IPPROTO_TCP)
+//         return 0;
+//     if (tcp->source == BLOCKED_TCP_PORT || tcp->dest == BLOCKED_TCP_PORT)
+//         return -1;
+//     return 0;
+// }
+// char _license[] SEC(".license") = "GPL";
+// ```
+//
+// This program, once compiled, 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. 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.
+
+#[test]
+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 ref prog = &text_scn.data;
+    // ---
+
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x79, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+
+    let mut packet = vec![
+        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,
+        0x7f, 0x00, 0x00, 0x01,
+        // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0.
+        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, 0x0au8
+    ];
+
+    let mut vm = rbpf::EbpfVmFixedMbuff::new(&prog, 0x40, 0x50);
+    vm.register_helper(helpers::BPF_TRACE_PRINTF_IDX, helpers::bpf_trace_printf);
+
+    let res = vm.prog_exec(&mut packet);
+    println!("Program returned: {:?} ({:#x})", res, res);
+    assert_eq!(res, 0xffffffff);
+}
+
+#[test]
+fn test_jit_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 ref prog = &text_scn.data;
+    // ---
+
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x79, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+
+    let mut packet = vec![
+        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,
+        0x7f, 0x00, 0x00, 0x01,
+        // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0.
+        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, 0x0au8
+    ];
+
+    let mut vm = rbpf::EbpfVmFixedMbuff::new(&prog, 0x40, 0x50);
+    vm.register_helper(helpers::BPF_TRACE_PRINTF_IDX, helpers::bpf_trace_printf);
+    vm.jit_compile();
+
+    let res = vm.prog_exec_jit(&mut packet);
+    println!("Program returned: {:?} ({:#x})", res, res);
+    assert_eq!(res, 0xffffffff);
+}
+
+// Program and memory come from uBPF test ldxh.
+#[test]
+fn test_vm_mbuff() {
+    let prog = vec![
+        // 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, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
+    ];
+
+    let mbuff = vec![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;
+    }
+
+    let vm = rbpf::EbpfVmMbuff::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem, mbuff), 0x2211);
+}
+
+// Program and memory come from uBPF test ldxh.
+#[test]
+fn test_jit_mbuff() {
+    let prog = vec![
+        // 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, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
+    ];
+
+    let mbuff = vec![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;
+    }
+
+    let mut vm = rbpf::EbpfVmMbuff::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem, mbuff), 0x2211);
+}

+ 1872 - 0
tests/ubpf_jit_x86_64.rs

@@ -0,0 +1,1872 @@
+// Converted from the tests for uBPF <https://github.com/iovisor/ubpf>
+// Copyright 2015 Big Switch Networks, Inc
+// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+// 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.
+
+
+extern crate rbpf;
+
+use rbpf::helpers;
+
+#[test]
+fn test_jit_add() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x3);
+}
+
+#[test]
+fn test_jit_alu64_arith() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0xb7, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+        0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+        0x0f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x17, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+        0x1f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x2f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x37, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x3f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x2a);
+}
+
+#[test]
+fn test_jit_alu64_bit() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x47, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00,
+        0x57, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00,
+        0xb7, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00,
+        0x5f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+        0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x77, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x77, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+        0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xa7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xaf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x11);
+}
+
+#[test]
+fn test_jit_alu_arith() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb4, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb4, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb4, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb4, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb4, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb4, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0xb4, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+        0x04, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+        0x0c, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+        0x1c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x24, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x2c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x3c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x2a);
+}
+
+#[test]
+fn test_jit_alu_bit() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb4, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb4, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb4, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb4, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb4, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb4, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x4c, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x44, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00,
+        0x54, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00,
+        0xb4, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00,
+        0x5c, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x64, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+        0x6c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x74, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+        0x7c, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xa4, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xac, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x11);
+}
+
+#[test]
+fn test_jit_arsh32_high_shift() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x18, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xcc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x4);
+}
+
+#[test]
+fn test_jit_arsh() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
+        0x64, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+        0xc4, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xffff8000);
+}
+
+#[test]
+fn test_jit_arsh64() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+        0xc7, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xcf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xfffffffffffffff8);
+}
+
+#[test]
+fn test_jit_arsh_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x64, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+        0xcc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xffff8000);
+}
+
+#[test]
+fn test_jit_be16() {
+    let prog = vec![
+        0x69, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1122);
+}
+
+#[test]
+fn test_jit_be16_high() {
+    let prog = vec![
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1122);
+}
+
+#[test]
+fn test_jit_be32() {
+    let prog = vec![
+        0x61, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22, 0x33, 0x44
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x11223344);
+}
+
+#[test]
+fn test_jit_be32_high() {
+    let prog = vec![
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x11223344);
+}
+
+#[test]
+fn test_jit_be64() {
+    let prog = vec![
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1122334455667788);
+}
+
+#[test]
+fn test_jit_call() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.register_helper(0, helpers::gather_bytes);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x0102030405);
+}
+
+#[test]
+fn test_jit_call_memfrob() {
+    let prog = vec![
+        0xbf, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x79, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.register_helper(1, helpers::memfrob);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x102292e2f2c0708);
+}
+
+// TODO: helpers::trash_registers needs asm!().
+// Try this again once asm!() is available in stable.
+//#[test]
+//fn test_jit_call_save() {
+    //let prog = vec![
+        //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(&prog);
+    //vm.register_helper(2, helpers::trash_registers);
+    //vm.jit_compile();
+    //assert_eq!(vm.prog_exec_jit(), 0x4321);
+//}
+
+#[test]
+fn test_jit_div32_high_divisor() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x18, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x3c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x3);
+}
+
+#[test]
+fn test_jit_div32_imm() {
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x3);
+}
+
+#[test]
+fn test_jit_div32_reg() {
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x3c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x3);
+}
+
+#[test]
+fn test_jit_div64_imm() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x37, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x300000000);
+}
+
+#[test]
+fn test_jit_div64_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x3f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x300000000);
+}
+
+#[test]
+fn test_jit_early_exit() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x3);
+}
+
+// uBPF limits the number of user functions at 64. We don't.
+//#[test]
+//fn test_jit_err_call_bad_imm() {
+//}
+
+#[test]
+#[should_panic(expected = "[JIT] Error: unknown helper function (id: 0x3f)")]
+fn test_jit_err_call_unreg() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    vm.prog_exec_jit();
+}
+
+// TODO: Should panic!() instead, but I could not make it panic in JIT-compiled code, so the
+// program returns -1 instead. We can make it write on stderr, though.
+#[test]
+//#[should_panic(expected = "[JIT] Error: division by 0")]
+fn test_jit_err_div64_by_zero_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x3f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xffffffffffffffff);
+}
+
+// TODO: Same remark as above
+#[test]
+//#[should_panic(expected = "[JIT] Error: division by 0")]
+fn test_jit_err_div_by_zero_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x3c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xffffffffffffffff);
+}
+
+// TODO: Same remark as above
+#[test]
+//#[should_panic(expected = "[JIT] Error: division by 0")]
+fn test_jit_err_mod64_by_zero_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x9f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xffffffffffffffff);
+}
+
+// TODO: Same remark as above
+#[test]
+//#[should_panic(expected = "[JIT] Error: division by 0")]
+fn test_jit_err_mod_by_zero_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x9c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xffffffffffffffff);
+}
+
+// TODO SKIP: JIT disabled for this testcase (stack oob check not implemented)
+// #[test]
+// #[should_panic(expected = "Error: out of bounds memory store (insn #1)")]
+// fn test_jit_err_stack_out_of_bound() {
+//     let prog = vec![
+//         0x72, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+//         0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+//     ];
+//     let mut vm = rbpf::EbpfVmNoData::new(&prog);
+//     vm.jit_compile();
+//     vm.prog_exec_jit();
+// }
+
+#[test]
+fn test_jit_exit() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x0);
+}
+
+#[test]
+fn test_jit_ja() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jeq_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0x15, 0x01, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0x15, 0x01, 0x01, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jeq_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0x1d, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0x1d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jge_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0x35, 0x01, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x35, 0x01, 0x01, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jgt_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x25, 0x01, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0x25, 0x01, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x25, 0x01, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jgt_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x2d, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x2d, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x2d, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jit_bounce() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xbf, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jne_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0x5d, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0x5d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jset_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x45, 0x01, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+        0x45, 0x01, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jset_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x4d, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+        0x4d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jsge_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+        0x75, 0x01, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0x75, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0x75, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jsge_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+        0xb7, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0xb4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7d, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7d, 0x31, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xbf, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jsgt_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+        0x65, 0x01, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x65, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_jsgt_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+        0xb7, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0x6d, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x6d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_lddw() {
+    let prog = vec![
+        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 mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1122334455667788);
+}
+
+#[test]
+fn test_jit_lddw2() {
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x80000000);
+}
+
+#[test]
+fn test_jit_ldxb_all() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x71, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x71, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x06, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x71, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x71, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+        0x71, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+        0x71, 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x02, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+        0x71, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x71, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+        0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 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 mem = vec![
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x9876543210);
+}
+
+#[test]
+fn test_jit_ldxb() {
+    let prog = vec![
+        0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x11);
+}
+
+#[test]
+fn test_jit_ldxdw() {
+    let prog = vec![
+        0x79, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+        0x77, 0x88, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x8877665544332211);
+}
+
+#[test]
+fn test_jit_ldxh_all() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x09, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x69, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x07, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x69, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x06, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x06, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x69, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+        0x69, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+        0x69, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x02, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+        0x69, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+        0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 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 mem = vec![
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03,
+        0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07,
+        0x00, 0x08, 0x00, 0x09
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x9876543210);
+}
+
+#[test]
+fn test_jit_ldxh_all2() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x09, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x07, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x06, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 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 mem = vec![
+        0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08,
+        0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80,
+        0x01, 0x00, 0x02, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x3ff);
+}
+
+#[test]
+fn test_jit_ldxh() {
+    let prog = vec![
+        0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x2211);
+}
+
+#[test]
+fn test_jit_ldxh_same_reg() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x6a, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
+        0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xff, 0xff
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1234);
+}
+
+#[test]
+fn test_jit_ldxw_all() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x61, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x09, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x08, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x06, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x03, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 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 mem = vec![
+        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
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x030f0f);
+}
+
+#[test]
+fn test_jit_ldxw() {
+    let prog = vec![
+        0x61, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x44332211);
+}
+
+#[test]
+fn test_jit_le16() {
+    let prog = vec![
+        0x69, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xd4, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x22, 0x11
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1122);
+}
+
+#[test]
+fn test_jit_le32() {
+    let prog = vec![
+        0x61, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xd4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x44, 0x33, 0x22, 0x11
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x11223344);
+}
+
+#[test]
+fn test_jit_le64() {
+    let prog = vec![
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xd4, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1122334455667788);
+}
+
+#[test]
+fn test_jit_lsh_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x6f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x10);
+}
+
+#[test]
+fn test_jit_mod() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x74, 0x16, 0x00, 0x00,
+        0x94, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+        0x9c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x5);
+}
+
+#[test]
+fn test_jit_mod32() {
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x94, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x0);
+}
+
+#[test]
+fn test_jit_mod64() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x36, 0x84, 0x85, 0xb1,
+        0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x47, 0x00, 0x00, 0x00, 0xc8, 0xc5, 0x0d, 0x10,
+        0xb4, 0x01, 0x00, 0x00, 0x3e, 0x26, 0xde, 0x0d,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x47, 0x01, 0x00, 0x00, 0xf3, 0xf7, 0xbe, 0x3c,
+        0x9f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x97, 0x00, 0x00, 0x00, 0x78, 0x17, 0x8f, 0x65,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x30ba5a04);
+}
+
+#[test]
+fn test_jit_mov() {
+    let prog = vec![
+        0xb4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xbc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_mul32_imm() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xc);
+}
+
+#[test]
+fn test_jit_mul32_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x2c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xc);
+}
+
+#[test]
+fn test_jit_mul32_reg_overflow() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x2c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x4);
+}
+
+#[test]
+fn test_jit_mul64_imm() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40,
+        0x27, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x100000004);
+}
+
+#[test]
+fn test_jit_mul64_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x2f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x100000004);
+}
+
+#[test]
+fn test_jit_mul_loop() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x77, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x15, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0x55, 0x01, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x75db9c97);
+}
+
+#[test]
+fn test_jit_neg64() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xfffffffffffffffe);
+}
+
+#[test]
+fn test_jit_neg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xfffffffe);
+}
+
+#[test]
+fn test_jit_prime() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x25, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x3d, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x3f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x2f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x1f, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x55, 0x04, 0xf6, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_rhs32() {
+    let prog = vec![
+        0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x74, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x00ffffff);
+}
+
+#[test]
+fn test_jit_rsh_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x1);
+}
+
+#[test]
+fn test_jit_stack() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+        0x7a, 0x0a, 0xf0, 0xff, 0xab, 0x00, 0x00, 0x00,
+        0x7a, 0x0a, 0xf8, 0xff, 0xcd, 0x00, 0x00, 0x00,
+        0x57, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xbf, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x0f, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x79, 0x20, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0xcd);
+}
+
+#[test]
+fn test_jit_stack2() {
+    let prog = vec![
+        0x72, 0x0a, 0xfc, 0xff, 0x01, 0x00, 0x00, 0x00,
+        0x72, 0x0a, 0xfd, 0xff, 0x02, 0x00, 0x00, 0x00,
+        0x72, 0x0a, 0xfe, 0xff, 0x03, 0x00, 0x00, 0x00,
+        0x72, 0x0a, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,
+        0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x1f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0xa2, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0xa3, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0xa4, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0xa5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xa7, 0x00, 0x00, 0x00, 0x2a, 0x2a, 0x2a, 0x2a,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.register_helper(0, helpers::gather_bytes);
+    vm.register_helper(1, helpers::memfrob);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x01020304);
+}
+
+#[test]
+fn test_jit_stb() {
+    let prog = vec![
+        0x72, 0x01, 0x02, 0x00, 0x11, 0x00, 0x00, 0x00,
+        0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x11);
+}
+
+#[test]
+fn test_jit_stdw() {
+    let prog = vec![
+        0x7a, 0x01, 0x02, 0x00, 0x11, 0x22, 0x33, 0x44,
+        0x79, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x44332211);
+}
+
+#[test]
+fn test_jit_sth() {
+    let prog = vec![
+        0x6a, 0x01, 0x02, 0x00, 0x11, 0x22, 0x00, 0x00,
+        0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x2211);
+}
+
+#[test]
+fn test_jit_string_stack() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x61, 0x62, 0x63, 0x78,
+        0x63, 0x1a, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x6a, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x6a, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x61, 0x62, 0x63, 0x79,
+        0x63, 0x1a, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff,
+        0xbf, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x77, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x55, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff,
+        0xbf, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x02, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff,
+        0x85, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x77, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x1d, 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.register_helper(4, helpers::strcmp);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(), 0x0);
+}
+
+#[test]
+fn test_jit_stw() {
+    let prog = vec![
+        0x62, 0x01, 0x02, 0x00, 0x11, 0x22, 0x33, 0x44,
+        0x61, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x44332211);
+}
+
+#[test]
+fn test_jit_stxb() {
+    let prog = vec![
+        0xb4, 0x02, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+        0x73, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x11);
+}
+
+#[test]
+fn test_jit_stxb_all() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0xf2, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0xf3, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00,
+        0xb7, 0x06, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00,
+        0xb7, 0x08, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
+        0x73, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x31, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x41, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x61, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x71, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x81, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0xf0f2f3f4f5f6f7f8);
+}
+
+#[test]
+fn test_jit_stxb_all2() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00,
+        0xb7, 0x09, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00,
+        0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xff, 0xff
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0xf1f9);
+}
+
+#[test]
+fn test_jit_stxb_chain() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x70, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x50, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x30, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x10, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x2a);
+}
+
+#[test]
+fn test_jit_stxdw() {
+    let prog = vec![
+        0xb7, 0x02, 0x00, 0x00, 0x55, 0x66, 0x77, 0x88,
+        0x67, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x47, 0x02, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44,
+        0x7b, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x79, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x8877665544332211);
+}
+
+#[test]
+fn test_jit_stxh() {
+    let prog = vec![
+        0xb4, 0x02, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00,
+        0x6b, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x2211);
+}
+
+#[test]
+fn test_jit_stxw() {
+    let prog = vec![
+        0xb4, 0x02, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44,
+        0x63, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x61, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x44332211);
+}
+
+#[test]
+fn test_jit_subnet() {
+    let prog = vec![
+        0xb7, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+        0x69, 0x13, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x55, 0x03, 0x02, 0x00, 0x81, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+        0x69, 0x13, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x57, 0x03, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
+        0x55, 0x03, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x61, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x57, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
+        0x15, 0x01, 0x01, 0x00, 0xc0, 0xa8, 0x01, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        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
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1);
+}
+
+
+const PROG_TCP_PORT_80: [u8;152] = [
+    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, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00,
+    0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x55, 0x02, 0x0a, 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,
+    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
+];
+
+#[test]
+fn test_jit_tcp_port80_match() {
+    let mut mem = vec![
+        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, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44
+    ];
+    let prog = &PROG_TCP_PORT_80.to_vec();
+    let mut vm = rbpf::EbpfVmRaw::new(prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1);
+}
+
+#[test]
+fn test_jit_tcp_port80_nomatch() {
+    let mut mem = vec![
+        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, 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, 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, 0x44, 0x44, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44
+    ];
+    let prog = &PROG_TCP_PORT_80.to_vec();
+    let mut vm = rbpf::EbpfVmRaw::new(prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x0);
+}
+
+#[test]
+fn test_jit_tcp_port80_nomatch_ethertype() {
+    let mut mem = vec![
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06,
+        0x07, 0x08, 0x09, 0x0a, 0x08, 0x01, 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, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44
+    ];
+    let prog = &PROG_TCP_PORT_80.to_vec();
+    let mut vm = rbpf::EbpfVmRaw::new(prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x0);
+}
+
+#[test]
+fn test_jit_tcp_port80_nomatch_proto() {
+    let mut mem = vec![
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06,
+        0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, 0x00,
+        0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+        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, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44
+    ];
+    let prog = &PROG_TCP_PORT_80.to_vec();
+    let mut vm = rbpf::EbpfVmRaw::new(prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x0);
+}
+
+const PROG_TCP_SACK: [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
+];
+
+#[test]
+fn test_jit_tcp_sack_match() {
+    let mut mem = vec![
+        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
+    ];
+    let prog = &PROG_TCP_SACK.to_vec();
+    let mut vm = rbpf::EbpfVmRaw::new(prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x1);
+}
+
+#[test]
+fn test_jit_tcp_sack_nomatch() {
+    let mut mem = vec![
+        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, 0x80, 0x10,
+        0x01, 0x3e, 0x34, 0xb6, 0x00, 0x00, 0x01, 0x01,
+        0x08, 0x0a, 0x00, 0x17, 0x95, 0x6f, 0x8d, 0x9d,
+        0x9e, 0x27
+    ];
+    let prog = &PROG_TCP_SACK.to_vec();
+    let mut vm = rbpf::EbpfVmRaw::new(prog);
+    vm.jit_compile();
+    assert_eq!(vm.prog_exec_jit(&mut mem), 0x0);
+}

+ 166 - 0
tests/ubpf_verifier.rs

@@ -0,0 +1,166 @@
+// Converted from the tests for uBPF <https://github.com/iovisor/ubpf>
+// Copyright 2015 Big Switch Networks, Inc
+// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+// 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_verifier_<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 “verifier”.
+
+
+extern crate rbpf;
+
+use rbpf::ebpf;
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: division by 0 (insn #1)")]
+fn test_verifier_err_div_by_zero_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: unsupported argument for LE/BE (insn #0)")]
+fn test_verifier_err_endian_size() {
+    let prog = vec![
+        0xdc, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[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
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: infinite loop")]
+fn test_verifier_err_infinite_loop() {
+    let prog = vec![
+        0x05, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: invalid destination register (insn #0)")]
+fn test_verifier_err_invalid_reg_dst() {
+    let prog = vec![
+        0xb7, 0x0b, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: invalid source register (insn #0)")]
+fn test_verifier_err_invalid_reg_src() {
+    let prog = vec![
+        0xbf, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: jump to middle of LD_DW at #2 (insn #0)")]
+fn test_verifier_err_jmp_lddw() {
+    let prog = vec![
+        0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        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 vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: jump out of code to #3 (insn #0)")]
+fn test_verifier_err_jmp_out() {
+    let prog = vec![
+        0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: program does not end with “EXIT” instruction")]
+fn test_verifier_err_no_exit() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: eBPF program length limited to 4096, here 4097")]
+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..(4096 * ebpf::INSN_SIZE)).map( |x| match x % 8 {
+            0 => 0xb7,
+            1 => 0x01,
+            _ => 0
+    }).collect::<Vec<u8>>();
+    prog.append(&mut vec![ 0x95, 0, 0, 0, 0, 0, 0, 0 ]);
+
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: unknown eBPF opcode 0x6 (insn #0)")]
+fn test_verifier_err_unknown_opcode() {
+    let prog = vec![
+        0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "[Verifier] Error: cannot write into register r10 (insn #0)")]
+fn test_verifier_err_write_r10() {
+    let prog = vec![
+        0xb7, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}

+ 1771 - 0
tests/ubpf_vm.rs

@@ -0,0 +1,1771 @@
+// Converted from the tests for uBPF <https://github.com/iovisor/ubpf>
+// Copyright 2015 Big Switch Networks, Inc
+// Copyright 2016 6WIND S.A. <quentin.monnet@6wind.com>
+//
+// Licensed under the Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0> or
+// the MIT license <http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+
+// 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_vm_<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 interpreter.
+
+
+extern crate rbpf;
+
+use rbpf::helpers;
+
+#[test]
+fn test_vm_add() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x3);
+}
+
+#[test]
+fn test_vm_alu64_arith() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0xb7, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+        0x07, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+        0x0f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x17, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+        0x1f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x2f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x37, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x3f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x2a);
+}
+
+#[test]
+fn test_vm_alu64_bit() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x47, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00,
+        0x57, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00,
+        0xb7, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00,
+        0x5f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+        0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x77, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x77, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+        0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xa7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xaf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x11);
+}
+
+#[test]
+fn test_vm_alu_arith() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb4, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb4, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb4, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb4, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb4, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb4, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0xb4, 0x09, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+        0x04, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+        0x0c, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x14, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+        0x1c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x24, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x2c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x34, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x3c, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x2a);
+}
+
+#[test]
+fn test_vm_alu_bit() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb4, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb4, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb4, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb4, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb4, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb4, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x4c, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x44, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00,
+        0x54, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00,
+        0xb4, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00,
+        0x5c, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x64, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+        0x6c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x74, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+        0x7c, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xa4, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xac, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x11);
+}
+
+#[test]
+fn test_vm_arsh32_high_shift() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x18, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xcc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x4);
+}
+
+#[test]
+fn test_vm_arsh() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
+        0x64, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+        0xc4, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0xffff8000);
+}
+
+#[test]
+fn test_vm_arsh64() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+        0xc7, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xcf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0xfffffffffffffff8);
+}
+
+#[test]
+fn test_vm_arsh_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x64, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+        0xcc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0xffff8000);
+}
+
+#[test]
+fn test_vm_be16() {
+    let prog = vec![
+        0x69, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1122);
+}
+
+#[test]
+fn test_vm_be16_high() {
+    let prog = vec![
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1122);
+}
+
+#[test]
+fn test_vm_be32() {
+    let prog = vec![
+        0x61, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22, 0x33, 0x44
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x11223344);
+}
+
+#[test]
+fn test_vm_be32_high() {
+    let prog = vec![
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x11223344);
+}
+
+#[test]
+fn test_vm_be64() {
+    let prog = vec![
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1122334455667788);
+}
+
+#[test]
+fn test_vm_call() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.register_helper(0, helpers::gather_bytes);
+    assert_eq!(vm.prog_exec(), 0x0102030405);
+}
+
+#[test]
+fn test_vm_call_memfrob() {
+    let prog = vec![
+        0xbf, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x79, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
+    ];
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
+    vm.register_helper(1, helpers::memfrob);
+    assert_eq!(vm.prog_exec(&mut mem), 0x102292e2f2c0708);
+}
+
+// TODO: helpers::trash_registers needs asm!().
+// Try this again once asm!() is available in stable.
+//#[test]
+//fn test_vm_call_save() {
+    //let prog = vec![
+        //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(&prog);
+    //vm.register_helper(2, helpers::trash_registers);
+    //assert_eq!(vm.prog_exec(), 0x4321);
+//}
+
+#[test]
+fn test_vm_div32_high_divisor() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x18, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x3c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x3);
+}
+
+#[test]
+fn test_vm_div32_imm() {
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x34, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x3);
+}
+
+#[test]
+fn test_vm_div32_reg() {
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x3c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x3);
+}
+
+#[test]
+fn test_vm_div64_imm() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x37, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x300000000);
+}
+
+#[test]
+fn test_vm_div64_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x3f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x300000000);
+}
+
+#[test]
+fn test_vm_early_exit() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x3);
+}
+
+// uBPF limits the number of user functions at 64. We don't.
+//#[test]
+//fn test_vm_err_call_bad_imm() {
+//}
+
+#[test]
+#[should_panic(expected = "Error: unknown helper function (id: 0x3f)")]
+fn test_vm_err_call_unreg() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "Error: division by 0")]
+fn test_vm_err_div64_by_zero_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x3f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "Error: division by 0")]
+fn test_vm_err_div_by_zero_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x3c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "Error: division by 0")]
+fn test_vm_err_mod64_by_zero_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x9f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "Error: division by 0")]
+fn test_vm_err_mod_by_zero_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x9c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+#[should_panic(expected = "Error: out of bounds memory store (insn #1)")]
+fn test_vm_err_stack_out_of_bound() {
+    let prog = vec![
+        0x72, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.prog_exec();
+}
+
+#[test]
+fn test_vm_exit() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x0);
+}
+
+#[test]
+fn test_vm_ja() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jeq_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0x15, 0x01, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0x15, 0x01, 0x01, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jeq_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0x1d, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0x1d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jge_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0x35, 0x01, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x35, 0x01, 0x01, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jgt_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x25, 0x01, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0x25, 0x01, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x25, 0x01, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jgt_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x2d, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x2d, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x2d, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jit_bounce() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xbf, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jne_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+        0x5d, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0x5d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jset_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x45, 0x01, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+        0x45, 0x01, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jset_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0xb4, 0x02, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x4d, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+        0x4d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jsge_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+        0x75, 0x01, 0x05, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0x75, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0x75, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jsge_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+        0xb7, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0xb4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7d, 0x21, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7d, 0x31, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xbf, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x7d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jsgt_imm() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+        0x65, 0x01, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x65, 0x01, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jsgt_reg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff,
+        0xb7, 0x02, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0x6d, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x6d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_lddw() {
+    let prog = vec![
+        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 vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1122334455667788);
+}
+
+#[test]
+fn test_vm_lddw2() {
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x80000000);
+}
+
+#[test]
+fn test_vm_ldxb_all() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x71, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x71, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x06, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x71, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x71, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+        0x71, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+        0x71, 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x02, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+        0x71, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x71, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+        0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 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 mem = vec![
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x9876543210);
+}
+
+#[test]
+fn test_vm_ldxb() {
+    let prog = vec![
+        0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x11);
+}
+
+#[test]
+fn test_vm_ldxdw() {
+    let prog = vec![
+        0x79, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+        0x77, 0x88, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x8877665544332211);
+}
+
+#[test]
+fn test_vm_ldxh_all() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x09, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x69, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x07, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x07, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x69, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x06, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x06, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+        0x69, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x04, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+        0x69, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x03, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
+        0x69, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x02, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+        0x69, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x67, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+        0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 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 mem = vec![
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03,
+        0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07,
+        0x00, 0x08, 0x00, 0x09
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x9876543210);
+}
+
+#[test]
+fn test_vm_ldxh_all2() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x09, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x08, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x07, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x06, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x04, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x03, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 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 mem = vec![
+        0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08,
+        0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80,
+        0x01, 0x00, 0x02, 0x00
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x3ff);
+}
+
+#[test]
+fn test_vm_ldxh() {
+    let prog = vec![
+        0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x2211);
+}
+
+#[test]
+fn test_vm_ldxh_same_reg() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x6a, 0x00, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
+        0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xff, 0xff
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1234);
+}
+
+#[test]
+fn test_vm_ldxw_all() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x61, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x09, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x08, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x06, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x05, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x03, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x61, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x4f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x4f, 0x50, 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 mem = vec![
+        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
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x030f0f);
+}
+
+#[test]
+fn test_vm_ldxw() {
+    let prog = vec![
+        0x61, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x44332211);
+}
+
+#[test]
+fn test_vm_le16() {
+    let prog = vec![
+        0x69, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xd4, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x22, 0x11
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1122);
+}
+
+#[test]
+fn test_vm_le32() {
+    let prog = vec![
+        0x61, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xd4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x44, 0x33, 0x22, 0x11
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x11223344);
+}
+
+#[test]
+fn test_vm_le64() {
+    let prog = vec![
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xd4, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1122334455667788);
+}
+
+#[test]
+fn test_vm_lsh_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x6f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x10);
+}
+
+#[test]
+fn test_vm_mod() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x74, 0x16, 0x00, 0x00,
+        0x94, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00,
+        0xb4, 0x01, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+        0x9c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x5);
+}
+
+#[test]
+fn test_vm_mod32() {
+    let prog = vec![
+        0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x94, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x0);
+}
+
+#[test]
+fn test_vm_mod64() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x36, 0x84, 0x85, 0xb1,
+        0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x47, 0x00, 0x00, 0x00, 0xc8, 0xc5, 0x0d, 0x10,
+        0xb4, 0x01, 0x00, 0x00, 0x3e, 0x26, 0xde, 0x0d,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x47, 0x01, 0x00, 0x00, 0xf3, 0xf7, 0xbe, 0x3c,
+        0x9f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x97, 0x00, 0x00, 0x00, 0x78, 0x17, 0x8f, 0x65,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x30ba5a04);
+}
+
+#[test]
+fn test_vm_mov() {
+    let prog = vec![
+        0xb4, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xbc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_mul32_imm() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x24, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0xc);
+}
+
+#[test]
+fn test_vm_mul32_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x2c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0xc);
+}
+
+#[test]
+fn test_vm_mul32_reg_overflow() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x2c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x4);
+}
+
+#[test]
+fn test_vm_mul64_imm() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40,
+        0x27, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x100000004);
+}
+
+#[test]
+fn test_vm_mul64_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x40,
+        0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x2f, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x100000004);
+}
+
+#[test]
+fn test_vm_mul_loop() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x77, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x15, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x27, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+        0x55, 0x01, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x75db9c97);
+}
+
+#[test]
+fn test_vm_neg64() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0xfffffffffffffffe);
+}
+
+#[test]
+fn test_vm_neg() {
+    let prog = vec![
+        0xb4, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0xfffffffe);
+}
+
+#[test]
+fn test_vm_prime() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x25, 0x01, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x3d, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x3f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x2f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x1f, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x55, 0x04, 0xf6, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_rhs32() {
+    let prog = vec![
+        0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x74, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x00ffffff);
+}
+
+#[test]
+fn test_vm_rsh_reg() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_stack() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+        0x7a, 0x0a, 0xf0, 0xff, 0xab, 0x00, 0x00, 0x00,
+        0x7a, 0x0a, 0xf8, 0xff, 0xcd, 0x00, 0x00, 0x00,
+        0x57, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0xbf, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x0f, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x79, 0x20, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0xcd);
+}
+
+#[test]
+fn test_vm_stack2() {
+    let prog = vec![
+        0x72, 0x0a, 0xfc, 0xff, 0x01, 0x00, 0x00, 0x00,
+        0x72, 0x0a, 0xfd, 0xff, 0x02, 0x00, 0x00, 0x00,
+        0x72, 0x0a, 0xfe, 0xff, 0x03, 0x00, 0x00, 0x00,
+        0x72, 0x0a, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,
+        0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0x1f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0xa2, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0xa3, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0xa4, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0xa5, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xa7, 0x00, 0x00, 0x00, 0x2a, 0x2a, 0x2a, 0x2a,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.register_helper(0, helpers::gather_bytes);
+    vm.register_helper(1, helpers::memfrob);
+    assert_eq!(vm.prog_exec(), 0x01020304);
+}
+
+#[test]
+fn test_vm_stb() {
+    let prog = vec![
+        0x72, 0x01, 0x02, 0x00, 0x11, 0x00, 0x00, 0x00,
+        0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x11);
+}
+
+#[test]
+fn test_vm_stdw() {
+    let prog = vec![
+        0x7a, 0x01, 0x02, 0x00, 0x11, 0x22, 0x33, 0x44,
+        0x79, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x44332211);
+}
+
+#[test]
+fn test_vm_sth() {
+    let prog = vec![
+        0x6a, 0x01, 0x02, 0x00, 0x11, 0x22, 0x00, 0x00,
+        0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x2211);
+}
+
+#[test]
+fn test_vm_string_stack() {
+    let prog = vec![
+        0xb7, 0x01, 0x00, 0x00, 0x61, 0x62, 0x63, 0x78,
+        0x63, 0x1a, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x6a, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x6a, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0x61, 0x62, 0x63, 0x79,
+        0x63, 0x1a, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff,
+        0xbf, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x85, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x77, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x55, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x01, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff,
+        0xbf, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x07, 0x02, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff,
+        0x85, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+        0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x77, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x1d, 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.register_helper(4, helpers::strcmp);
+    assert_eq!(vm.prog_exec(), 0x0);
+}
+
+#[test]
+fn test_vm_stw() {
+    let prog = vec![
+        0x62, 0x01, 0x02, 0x00, 0x11, 0x22, 0x33, 0x44,
+        0x61, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x44332211);
+}
+
+#[test]
+fn test_vm_stxb() {
+    let prog = vec![
+        0xb4, 0x02, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+        0x73, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x11);
+}
+
+#[test]
+fn test_vm_stxb_all() {
+    let prog = vec![
+        0xb7, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0xf2, 0x00, 0x00, 0x00,
+        0xb7, 0x03, 0x00, 0x00, 0xf3, 0x00, 0x00, 0x00,
+        0xb7, 0x04, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00,
+        0xb7, 0x05, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00,
+        0xb7, 0x06, 0x00, 0x00, 0xf6, 0x00, 0x00, 0x00,
+        0xb7, 0x07, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00,
+        0xb7, 0x08, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00,
+        0x73, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x31, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x41, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x61, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x71, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x81, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x79, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0xf0f2f3f4f5f6f7f8);
+}
+
+#[test]
+fn test_vm_stxb_all2() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x01, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00,
+        0xb7, 0x09, 0x00, 0x00, 0xf9, 0x00, 0x00, 0x00,
+        0x73, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xff, 0xff
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0xf1f9);
+}
+
+#[test]
+fn test_vm_stxb_chain() {
+    let prog = vec![
+        0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x07, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x70, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x50, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x30, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x20, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x73, 0x10, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x71, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x2a);
+}
+
+#[test]
+fn test_vm_stxdw() {
+    let prog = vec![
+        0xb7, 0x02, 0x00, 0x00, 0x55, 0x66, 0x77, 0x88,
+        0x67, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+        0x47, 0x02, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44,
+        0x7b, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x79, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x8877665544332211);
+}
+
+#[test]
+fn test_vm_stxh() {
+    let prog = vec![
+        0xb4, 0x02, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00,
+        0x6b, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x2211);
+}
+
+#[test]
+fn test_vm_stxw() {
+    let prog = vec![
+        0xb4, 0x02, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44,
+        0x63, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x61, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x44332211);
+}
+
+#[test]
+fn test_vm_subnet() {
+    let prog = vec![
+        0xb7, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+        0x69, 0x13, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x55, 0x03, 0x02, 0x00, 0x81, 0x00, 0x00, 0x00,
+        0xb7, 0x02, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+        0x69, 0x13, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x57, 0x03, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
+        0x55, 0x03, 0x05, 0x00, 0x08, 0x00, 0x00, 0x00,
+        0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x61, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x57, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
+        0x15, 0x01, 0x01, 0x00, 0xc0, 0xa8, 0x01, 0x00,
+        0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+    ];
+    let mut mem = vec![
+        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
+    ];
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1);
+}
+
+
+const PROG_TCP_PORT_80: [u8;152] = [
+    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, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00,
+    0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x55, 0x02, 0x0a, 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,
+    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
+];
+
+#[test]
+fn test_vm_tcp_port80_match() {
+    let mut mem = vec![
+        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, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44
+    ];
+    let prog = &PROG_TCP_PORT_80.to_vec();
+    let vm = rbpf::EbpfVmRaw::new(prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1);
+}
+
+#[test]
+fn test_vm_tcp_port80_nomatch() {
+    let mut mem = vec![
+        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, 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, 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, 0x44, 0x44, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44
+    ];
+    let prog = &PROG_TCP_PORT_80.to_vec();
+    let vm = rbpf::EbpfVmRaw::new(prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x0);
+}
+
+#[test]
+fn test_vm_tcp_port80_nomatch_ethertype() {
+    let mut mem = vec![
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06,
+        0x07, 0x08, 0x09, 0x0a, 0x08, 0x01, 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, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44
+    ];
+    let prog = &PROG_TCP_PORT_80.to_vec();
+    let vm = rbpf::EbpfVmRaw::new(prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x0);
+}
+
+#[test]
+fn test_vm_tcp_port80_nomatch_proto() {
+    let mut mem = vec![
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06,
+        0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, 0x00,
+        0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11,
+        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, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+        0x44, 0x44, 0x44, 0x44
+    ];
+    let prog = &PROG_TCP_PORT_80.to_vec();
+    let vm = rbpf::EbpfVmRaw::new(prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x0);
+}
+
+const PROG_TCP_SACK: [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
+];
+
+#[test]
+fn test_vm_tcp_sack_match() {
+    let mut mem = vec![
+        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
+    ];
+    let prog = &PROG_TCP_SACK.to_vec();
+    let vm = rbpf::EbpfVmRaw::new(prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x1);
+}
+
+#[test]
+fn test_vm_tcp_sack_nomatch() {
+    let mut mem = vec![
+        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, 0x80, 0x10,
+        0x01, 0x3e, 0x34, 0xb6, 0x00, 0x00, 0x01, 0x01,
+        0x08, 0x0a, 0x00, 0x17, 0x95, 0x6f, 0x8d, 0x9d,
+        0x9e, 0x27
+    ];
+    let prog = &PROG_TCP_SACK.to_vec();
+    let vm = rbpf::EbpfVmRaw::new(prog);
+    assert_eq!(vm.prog_exec(&mut mem), 0x0);
+}