فهرست منبع

Merge pull request #10 from rlane/assembler2

src/assembler.rs: add eBPF assembler

Also add tests for assembler, and use the assembler to build the eBPF programs in some exising tests (instead of byte arrays).

This assembler uses the ASM parser previously added to produce eBPF bytecode and build a program from the simple eBPF assembly language defined in uBPF. It is fully “backward“-compatible with uBPF language (not meaning uBPF is a “previous” version here), but also adds an alias on ALU operations with size prefix, i.e. `add` and `add64` are the same.

It should be able to assemble instructions dumped with the disassembler, but this has not been thoroughly tested yet (ok, not tested at all at merge time).
Quentin Monnet 8 سال پیش
والد
کامیت
e31c945be0
8فایلهای تغییر یافته به همراه881 افزوده شده و 161 حذف شده
  1. 5 3
      src/asm_parser.rs
  2. 235 0
      src/assembler.rs
  3. 1 0
      src/lib.rs
  4. 10 0
      tests/asm_parser.rs
  5. 474 0
      tests/assembler.rs
  6. 131 0
      tests/common.rs
  7. 11 76
      tests/ubpf_jit_x86_64.rs
  8. 14 82
      tests/ubpf_vm.rs

+ 5 - 3
src/asm_parser.rs

@@ -13,7 +13,7 @@ use combine::{between, eof, many, many1, one_of, optional, Parser, ParseError, P
 use combine::primitives::{Error, Info};
 
 /// Operand of an instruction.
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq)]
 pub enum Operand {
     /// Register number.
     Register(i64),
@@ -21,6 +21,8 @@ pub enum Operand {
     Integer(i64),
     /// Register number and offset.
     Memory(i64, i64),
+    /// Used for pattern matching.
+    Nil,
 }
 
 /// Parsed instruction.
@@ -47,7 +49,7 @@ fn integer<I>(input: I) -> ParseResult<i64, I>
     });
     let hex = string("0x")
         .with(many1(hex_digit()))
-        .map(|x: String| i64::from_str_radix(&x, 16).unwrap());
+        .map(|x: String| u64::from_str_radix(&x, 16).unwrap() as i64);
     let dec = many1(digit()).map(|x: String| i64::from_str_radix(&x, 10).unwrap());
     (sign, try(hex).or(dec)).map(|(s, x)| s * x).parse_stream(input)
 }
@@ -116,7 +118,7 @@ fn format_parse_error(parse_error: ParseError<State<&str>>) -> String {
 ///
 /// The instructions are not validated and may have invalid names and operand types.
 pub fn parse(input: &str) -> Result<Vec<Instruction>, String> {
-    match many(parser(instruction)).skip(eof()).parse(State::new(input)) {
+    match spaces().with(many(parser(instruction)).skip(eof())).parse(State::new(input)) {
         Ok((insts, _)) => Ok(insts),
         Err(err) => Err(format_parse_error(err)),
     }

+ 235 - 0
src/assembler.rs

@@ -0,0 +1,235 @@
+// Copyright 2017 Rich Lane <lanerl@gmail.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.
+
+
+//! This module translates eBPF assembly language to binary.
+
+use asm_parser::{Instruction, Operand, parse};
+use ebpf;
+use ebpf::Insn;
+use std::collections::HashMap;
+use self::InstructionType::{AluBinary, AluUnary, LoadAbs, LoadInd, LoadImm, LoadReg, StoreImm,
+                            StoreReg, JumpUnconditional, JumpConditional, Call, Endian, NoOperand};
+use asm_parser::Operand::{Integer, Memory, Register, Nil};
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum InstructionType {
+    AluBinary,
+    AluUnary,
+    LoadImm,
+    LoadAbs,
+    LoadInd,
+    LoadReg,
+    StoreImm,
+    StoreReg,
+    JumpUnconditional,
+    JumpConditional,
+    Call,
+    Endian(i64),
+    NoOperand,
+}
+
+fn make_instruction_map() -> HashMap<String, (InstructionType, u8)> {
+    let mut result = HashMap::new();
+
+    let alu_binary_ops = [("add", ebpf::BPF_ADD),
+                          ("sub", ebpf::BPF_SUB),
+                          ("mul", ebpf::BPF_MUL),
+                          ("div", ebpf::BPF_DIV),
+                          ("or", ebpf::BPF_OR),
+                          ("and", ebpf::BPF_AND),
+                          ("lsh", ebpf::BPF_LSH),
+                          ("rsh", ebpf::BPF_RSH),
+                          ("mod", ebpf::BPF_MOD),
+                          ("xor", ebpf::BPF_XOR),
+                          ("mov", ebpf::BPF_MOV),
+                          ("arsh", ebpf::BPF_ARSH)];
+
+    let mem_sizes =
+        [("w", ebpf::BPF_W), ("h", ebpf::BPF_H), ("b", ebpf::BPF_B), ("dw", ebpf::BPF_DW)];
+
+    let jump_conditions = [("jeq", ebpf::BPF_JEQ),
+                           ("jgt", ebpf::BPF_JGT),
+                           ("jge", ebpf::BPF_JGE),
+                           ("jset", ebpf::BPF_JSET),
+                           ("jne", ebpf::BPF_JNE),
+                           ("jsgt", ebpf::BPF_JSGT),
+                           ("jsge", ebpf::BPF_JSGE)];
+
+    {
+        let mut entry = |name: &str, inst_type: InstructionType, opc: u8| {
+            result.insert(name.to_string(), (inst_type, opc))
+        };
+
+        // Miscellaneous.
+        entry("exit", NoOperand, ebpf::EXIT);
+        entry("ja", JumpUnconditional, ebpf::JA);
+        entry("call", Call, ebpf::CALL);
+        entry("lddw", LoadImm, ebpf::LD_DW_IMM);
+
+        // AluUnary.
+        entry("neg", AluUnary, ebpf::NEG64);
+        entry("neg32", AluUnary, ebpf::NEG32);
+        entry("neg64", AluUnary, ebpf::NEG64);
+
+        // AluBinary.
+        for &(name, opc) in &alu_binary_ops {
+            entry(name, AluBinary, ebpf::BPF_ALU64 | opc);
+            entry(&format!("{}32", name), AluBinary, ebpf::BPF_ALU | opc);
+            entry(&format!("{}64", name), AluBinary, ebpf::BPF_ALU64 | opc);
+        }
+
+        // LoadAbs, LoadInd, LoadReg, StoreImm, and StoreReg.
+        for &(suffix, size) in &mem_sizes {
+            entry(&format!("ldabs{}", suffix),
+                  LoadAbs,
+                  ebpf::BPF_ABS | ebpf::BPF_LD | size);
+            entry(&format!("ldind{}", suffix),
+                  LoadInd,
+                  ebpf::BPF_IND | ebpf::BPF_LD | size);
+            entry(&format!("ldx{}", suffix),
+                  LoadReg,
+                  ebpf::BPF_MEM | ebpf::BPF_LDX | size);
+            entry(&format!("st{}", suffix),
+                  StoreImm,
+                  ebpf::BPF_MEM | ebpf::BPF_ST | size);
+            entry(&format!("stx{}", suffix),
+                  StoreReg,
+                  ebpf::BPF_MEM | ebpf::BPF_STX | size);
+        }
+
+        // JumpConditional.
+        for &(name, condition) in &jump_conditions {
+            entry(name, JumpConditional, ebpf::BPF_JMP | condition);
+        }
+
+        // Endian.
+        for &size in &[16, 32, 64] {
+            entry(&format!("be{}", size), Endian(size), ebpf::BE);
+            entry(&format!("le{}", size), Endian(size), ebpf::LE);
+        }
+    }
+
+    result
+}
+
+fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result<Insn, String> {
+    if dst < 0 || dst >= 16 {
+        return Err(format!("Invalid destination register {}", dst));
+    }
+    if dst < 0 || src >= 16 {
+        return Err(format!("Invalid source register {}", src));
+    }
+    if off < -32768 || off >= 32768 {
+        return Err(format!("Invalid offset {}", off));
+    }
+    if imm < -2147483648 || imm >= 2147483648 {
+        return Err(format!("Invalid immediate {}", imm));
+    }
+    Ok(Insn {
+        opc: opc,
+        dst: dst as u8,
+        src: src as u8,
+        off: off as i16,
+        imm: imm as i32,
+    })
+}
+
+// TODO Use slice patterns when available and remove this function.
+fn operands_tuple(operands: &[Operand]) -> Result<(Operand, Operand, Operand), String> {
+    match operands.len() {
+        0 => Ok((Nil, Nil, Nil)),
+        1 => Ok((operands[0], Nil, Nil)),
+        2 => Ok((operands[0], operands[1], Nil)),
+        3 => Ok((operands[0], operands[1], operands[2])),
+        _ => Err("Too many operands".to_string()),
+    }
+}
+
+fn encode(inst_type: InstructionType, opc: u8, operands: &[Operand]) -> Result<Insn, String> {
+    let (a, b, c) = try!(operands_tuple(operands));
+    match (inst_type, a, b, c) {
+        (AluBinary, Register(dst), Register(src), Nil) => insn(opc | ebpf::BPF_X, dst, src, 0, 0),
+        (AluBinary, Register(dst), Integer(imm), Nil) => insn(opc | ebpf::BPF_K, dst, 0, 0, imm),
+        (AluUnary, Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, 0),
+        (LoadAbs, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm),
+        (LoadInd, Register(src), Integer(imm), Nil) => insn(opc, 0, src, 0, imm),
+        (LoadReg, Register(dst), Memory(src, off), Nil) |
+        (StoreReg, Memory(dst, off), Register(src), Nil) => insn(opc, dst, src, off, 0),
+        (StoreImm, Memory(dst, off), Integer(imm), Nil) => insn(opc, dst, 0, off, imm),
+        (NoOperand, Nil, Nil, Nil) => insn(opc, 0, 0, 0, 0),
+        (JumpUnconditional, Integer(off), Nil, Nil) => insn(opc, 0, 0, off, 0),
+        (JumpConditional, Register(dst), Register(src), Integer(off)) => {
+            insn(opc | ebpf::BPF_X, dst, src, off, 0)
+        }
+        (JumpConditional, Register(dst), Integer(imm), Integer(off)) => {
+            insn(opc | ebpf::BPF_K, dst, 0, off, imm)
+        }
+        (Call, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm),
+        (Endian(size), Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, size),
+        (LoadImm, Register(dst), Integer(imm), Nil) => insn(opc, dst, 0, 0, (imm << 32) >> 32),
+        _ => Err(format!("Unexpected operands: {:?}", operands)),
+    }
+}
+
+fn assemble_internal(parsed: &[Instruction]) -> Result<Vec<Insn>, String> {
+    let instruction_map = make_instruction_map();
+    let mut result: Vec<Insn> = vec![];
+    for instruction in parsed {
+        let name = instruction.name.as_str();
+        match instruction_map.get(name) {
+            Some(&(inst_type, opc)) => {
+                match encode(inst_type, opc, &instruction.operands) {
+                    Ok(insn) => result.push(insn),
+                    Err(msg) => return Err(format!("Failed to encode {}: {}", name, msg)),
+                }
+                // Special case for lddw.
+                if let LoadImm = inst_type {
+                    if let Integer(imm) = instruction.operands[1] {
+                        result.push(insn(0, 0, 0, 0, imm >> 32).unwrap());
+                    }
+                }
+            }
+            None => return Err(format!("Invalid instruction {:?}", name)),
+        }
+    }
+    Ok(result)
+}
+
+/// Parse assembly source and translate to binary.
+///
+/// # Examples
+///
+/// ```
+/// use rbpf::assembler::assemble;
+/// let prog = assemble("add64 r1, 0x605
+///                      mov64 r2, 0x32
+///                      mov64 r1, r0
+///                      be16 r0
+///                      neg64 r8
+///                      exit");
+/// println!("{:?}", prog);
+/// ```
+///
+/// This will produce the following output:
+///
+/// ```test
+/// Ok([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00,
+///     0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00,
+///     0xbf, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+///     0xdc, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+///     0x87, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+///     0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
+/// ```
+pub fn assemble(src: &str) -> Result<Vec<u8>, String> {
+    let parsed = try!(parse(src));
+    let insns = try!(assemble_internal(&parsed));
+    let mut result: Vec<u8> = vec![];
+    for insn in insns {
+        result.extend_from_slice(&insn.to_array());
+    }
+    Ok(result)
+}

+ 1 - 0
src/lib.rs

@@ -21,6 +21,7 @@ use std::collections::HashMap;
 extern crate libc;
 extern crate combine;
 
+pub mod assembler;
 pub mod asm_parser;
 pub mod disassembler;
 pub mod ebpf;

+ 10 - 0
tests/asm_parser.rs

@@ -311,3 +311,13 @@ fn test_error_unexpected_character() {
                Err("Parse error at line 2 column 1: unexpected '^', expected end of input"
                    .to_string()));
 }
+
+#[test]
+fn test_initial_whitespace() {
+    assert_eq!(parse(" 
+                      exit"),
+               Ok(vec![Instruction {
+                           name: "exit".to_string(),
+                           operands: vec![],
+                       }]));
+}

+ 474 - 0
tests/assembler.rs

@@ -0,0 +1,474 @@
+// Copyright 2017 Rich Lane <lanerl@gmail.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.
+extern crate rbpf;
+mod common;
+
+use common::{TCP_SACK_ASM, TCP_SACK_BIN};
+use rbpf::assembler::assemble;
+use rbpf::ebpf;
+
+fn asm(src: &str) -> Result<Vec<ebpf::Insn>, String> {
+    Ok(ebpf::to_insn_vec(&try!(assemble(src))))
+}
+
+fn insn(opc: u8, dst: u8, src: u8, off: i16, imm: i32) -> ebpf::Insn {
+    ebpf::Insn {
+        opc: opc,
+        dst: dst,
+        src: src,
+        off: off,
+        imm: imm,
+    }
+}
+
+#[test]
+fn test_empty() {
+    assert_eq!(asm(""), Ok(vec![]));
+}
+
+// Example for InstructionType::NoOperand.
+#[test]
+fn test_exit() {
+    assert_eq!(asm("exit"), Ok(vec![insn(ebpf::EXIT, 0, 0, 0, 0)]));
+}
+
+// Example for InstructionType::AluBinary.
+#[test]
+fn test_add64() {
+    assert_eq!(asm("add64 r1, r3"),
+               Ok(vec![insn(ebpf::ADD64_REG, 1, 3, 0, 0)]));
+    assert_eq!(asm("add64 r1, 5"),
+               Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, 5)]));
+}
+
+// Example for InstructionType::AluUnary.
+#[test]
+fn test_neg64() {
+    assert_eq!(asm("neg64 r1"), Ok(vec![insn(ebpf::NEG64, 1, 0, 0, 0)]));
+}
+
+// Example for InstructionType::LoadReg.
+#[test]
+fn test_ldxw() {
+    assert_eq!(asm("ldxw r1, [r2+5]"),
+               Ok(vec![insn(ebpf::LD_W_REG, 1, 2, 5, 0)]));
+}
+
+// Example for InstructionType::StoreImm.
+#[test]
+fn test_stw() {
+    assert_eq!(asm("stw [r2+5], 7"),
+               Ok(vec![insn(ebpf::ST_W_IMM, 2, 0, 5, 7)]));
+}
+
+// Example for InstructionType::StoreReg.
+#[test]
+fn test_stxw() {
+    assert_eq!(asm("stxw [r2+5], r8"),
+               Ok(vec![insn(ebpf::ST_W_REG, 2, 8, 5, 0)]));
+}
+
+// Example for InstructionType::JumpUnconditional.
+#[test]
+fn test_ja() {
+    assert_eq!(asm("ja +8"), Ok(vec![insn(ebpf::JA, 0, 0, 8, 0)]));
+    assert_eq!(asm("ja -3"), Ok(vec![insn(ebpf::JA, 0, 0, -3, 0)]));
+}
+
+// Example for InstructionType::JumpConditional.
+#[test]
+fn test_jeq() {
+    assert_eq!(asm("jeq r1, 4, +8"),
+               Ok(vec![insn(ebpf::JEQ_IMM, 1, 0, 8, 4)]));
+    assert_eq!(asm("jeq r1, r3, +8"),
+               Ok(vec![insn(ebpf::JEQ_REG, 1, 3, 8, 0)]));
+}
+
+// Example for InstructionType::Call.
+#[test]
+fn test_call() {
+    assert_eq!(asm("call 300"), Ok(vec![insn(ebpf::CALL, 0, 0, 0, 300)]));
+}
+
+// Example for InstructionType::Endian.
+#[test]
+fn test_be32() {
+    assert_eq!(asm("be32 r1"), Ok(vec![insn(ebpf::BE, 1, 0, 0, 32)]));
+}
+
+// Example for InstructionType::LoadImm.
+#[test]
+fn test_lddw() {
+    assert_eq!(asm("lddw r1, 0x1234abcd5678eeff"),
+               Ok(vec![insn(ebpf::LD_DW_IMM, 1, 0, 0, 0x5678eeff), insn(0, 0, 0, 0, 0x1234abcd)]));
+    assert_eq!(asm("lddw r1, 0xff11ee22dd33cc44"),
+               Ok(vec![insn(ebpf::LD_DW_IMM, 1, 0, 0, 0xdd33cc44u32 as i32),
+                       insn(0, 0, 0, 0, 0xff11ee22u32 as i32)]));
+}
+
+// Example for InstructionType::LoadAbs.
+#[test]
+fn test_ldabsw() {
+    assert_eq!(asm("ldabsw 1"), Ok(vec![insn(ebpf::LD_ABS_W, 0, 0, 0, 1)]));
+}
+
+// Example for InstructionType::LoadInd.
+#[test]
+fn test_ldindw() {
+    assert_eq!(asm("ldindw r1, 2"),
+               Ok(vec![insn(ebpf::LD_IND_W, 0, 1, 0, 2)]));
+}
+
+// Example for InstructionType::LoadReg.
+#[test]
+fn test_ldxdw() {
+    assert_eq!(asm("ldxdw r1, [r2+3]"),
+               Ok(vec![insn(ebpf::LD_DW_REG, 1, 2, 3, 0)]));
+}
+
+// Example for InstructionType::StoreImm.
+#[test]
+fn test_sth() {
+    assert_eq!(asm("sth [r1+2], 3"),
+               Ok(vec![insn(ebpf::ST_H_IMM, 1, 0, 2, 3)]));
+}
+
+// Example for InstructionType::StoreReg.
+#[test]
+fn test_stxh() {
+    assert_eq!(asm("stxh [r1+2], r3"),
+               Ok(vec![insn(ebpf::ST_H_REG, 1, 3, 2, 0)]));
+}
+
+// Test all supported AluBinary mnemonics.
+#[test]
+fn test_alu_binary() {
+    assert_eq!(asm("add r1, r2
+                    sub r1, r2
+                    mul r1, r2
+                    div r1, r2
+                    or r1, r2
+                    and r1, r2
+                    lsh r1, r2
+                    rsh r1, r2
+                    mod r1, r2
+                    xor r1, r2
+                    mov r1, r2
+                    arsh r1, r2"),
+               Ok(vec![insn(ebpf::ADD64_REG, 1, 2, 0, 0),
+                       insn(ebpf::SUB64_REG, 1, 2, 0, 0),
+                       insn(ebpf::MUL64_REG, 1, 2, 0, 0),
+                       insn(ebpf::DIV64_REG, 1, 2, 0, 0),
+                       insn(ebpf::OR64_REG, 1, 2, 0, 0),
+                       insn(ebpf::AND64_REG, 1, 2, 0, 0),
+                       insn(ebpf::LSH64_REG, 1, 2, 0, 0),
+                       insn(ebpf::RSH64_REG, 1, 2, 0, 0),
+                       insn(ebpf::MOD64_REG, 1, 2, 0, 0),
+                       insn(ebpf::XOR64_REG, 1, 2, 0, 0),
+                       insn(ebpf::MOV64_REG, 1, 2, 0, 0),
+                       insn(ebpf::ARSH64_REG, 1, 2, 0, 0)]));
+
+    assert_eq!(asm("add r1, 2
+                    sub r1, 2
+                    mul r1, 2
+                    div r1, 2
+                    or r1, 2
+                    and r1, 2
+                    lsh r1, 2
+                    rsh r1, 2
+                    mod r1, 2
+                    xor r1, 2
+                    mov r1, 2
+                    arsh r1, 2"),
+               Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::SUB64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MUL64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::DIV64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::OR64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::AND64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::LSH64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::RSH64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MOD64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::XOR64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MOV64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::ARSH64_IMM, 1, 0, 0, 2)]));
+
+    assert_eq!(asm("add64 r1, r2
+                    sub64 r1, r2
+                    mul64 r1, r2
+                    div64 r1, r2
+                    or64 r1, r2
+                    and64 r1, r2
+                    lsh64 r1, r2
+                    rsh64 r1, r2
+                    mod64 r1, r2
+                    xor64 r1, r2
+                    mov64 r1, r2
+                    arsh64 r1, r2"),
+               Ok(vec![insn(ebpf::ADD64_REG, 1, 2, 0, 0),
+                       insn(ebpf::SUB64_REG, 1, 2, 0, 0),
+                       insn(ebpf::MUL64_REG, 1, 2, 0, 0),
+                       insn(ebpf::DIV64_REG, 1, 2, 0, 0),
+                       insn(ebpf::OR64_REG, 1, 2, 0, 0),
+                       insn(ebpf::AND64_REG, 1, 2, 0, 0),
+                       insn(ebpf::LSH64_REG, 1, 2, 0, 0),
+                       insn(ebpf::RSH64_REG, 1, 2, 0, 0),
+                       insn(ebpf::MOD64_REG, 1, 2, 0, 0),
+                       insn(ebpf::XOR64_REG, 1, 2, 0, 0),
+                       insn(ebpf::MOV64_REG, 1, 2, 0, 0),
+                       insn(ebpf::ARSH64_REG, 1, 2, 0, 0)]));
+
+    assert_eq!(asm("add64 r1, 2
+                    sub64 r1, 2
+                    mul64 r1, 2
+                    div64 r1, 2
+                    or64 r1, 2
+                    and64 r1, 2
+                    lsh64 r1, 2
+                    rsh64 r1, 2
+                    mod64 r1, 2
+                    xor64 r1, 2
+                    mov64 r1, 2
+                    arsh64 r1, 2"),
+               Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::SUB64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MUL64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::DIV64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::OR64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::AND64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::LSH64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::RSH64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MOD64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::XOR64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MOV64_IMM, 1, 0, 0, 2),
+                       insn(ebpf::ARSH64_IMM, 1, 0, 0, 2)]));
+
+    assert_eq!(asm("add32 r1, r2
+                    sub32 r1, r2
+                    mul32 r1, r2
+                    div32 r1, r2
+                    or32 r1, r2
+                    and32 r1, r2
+                    lsh32 r1, r2
+                    rsh32 r1, r2
+                    mod32 r1, r2
+                    xor32 r1, r2
+                    mov32 r1, r2
+                    arsh32 r1, r2"),
+               Ok(vec![insn(ebpf::ADD32_REG, 1, 2, 0, 0),
+                       insn(ebpf::SUB32_REG, 1, 2, 0, 0),
+                       insn(ebpf::MUL32_REG, 1, 2, 0, 0),
+                       insn(ebpf::DIV32_REG, 1, 2, 0, 0),
+                       insn(ebpf::OR32_REG, 1, 2, 0, 0),
+                       insn(ebpf::AND32_REG, 1, 2, 0, 0),
+                       insn(ebpf::LSH32_REG, 1, 2, 0, 0),
+                       insn(ebpf::RSH32_REG, 1, 2, 0, 0),
+                       insn(ebpf::MOD32_REG, 1, 2, 0, 0),
+                       insn(ebpf::XOR32_REG, 1, 2, 0, 0),
+                       insn(ebpf::MOV32_REG, 1, 2, 0, 0),
+                       insn(ebpf::ARSH32_REG, 1, 2, 0, 0)]));
+
+    assert_eq!(asm("add32 r1, 2
+                    sub32 r1, 2
+                    mul32 r1, 2
+                    div32 r1, 2
+                    or32 r1, 2
+                    and32 r1, 2
+                    lsh32 r1, 2
+                    rsh32 r1, 2
+                    mod32 r1, 2
+                    xor32 r1, 2
+                    mov32 r1, 2
+                    arsh32 r1, 2"),
+               Ok(vec![insn(ebpf::ADD32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::SUB32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MUL32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::DIV32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::OR32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::AND32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::LSH32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::RSH32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MOD32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::XOR32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::MOV32_IMM, 1, 0, 0, 2),
+                       insn(ebpf::ARSH32_IMM, 1, 0, 0, 2)]));
+}
+
+// Test all supported AluUnary mnemonics.
+#[test]
+fn test_alu_unary() {
+    assert_eq!(asm("neg r1
+                    neg64 r1
+                    neg32 r1"),
+               Ok(vec![insn(ebpf::NEG64, 1, 0, 0, 0),
+                       insn(ebpf::NEG64, 1, 0, 0, 0),
+                       insn(ebpf::NEG32, 1, 0, 0, 0)]));
+}
+
+// Test all supported LoadAbs mnemonics.
+#[test]
+fn test_load_abs() {
+    assert_eq!(asm("ldabsw 1
+                    ldabsh 1
+                    ldabsb 1
+                    ldabsdw 1"),
+               Ok(vec![insn(ebpf::LD_ABS_W, 0, 0, 0, 1),
+                       insn(ebpf::LD_ABS_H, 0, 0, 0, 1),
+                       insn(ebpf::LD_ABS_B, 0, 0, 0, 1),
+                       insn(ebpf::LD_ABS_DW, 0, 0, 0, 1)]));
+}
+
+// Test all supported LoadInd mnemonics.
+#[test]
+fn test_load_ind() {
+    assert_eq!(asm("ldindw r1, 2
+                    ldindh r1, 2
+                    ldindb r1, 2
+                    ldinddw r1, 2"),
+               Ok(vec![insn(ebpf::LD_IND_W, 0, 1, 0, 2),
+                       insn(ebpf::LD_IND_H, 0, 1, 0, 2),
+                       insn(ebpf::LD_IND_B, 0, 1, 0, 2),
+                       insn(ebpf::LD_IND_DW, 0, 1, 0, 2)]));
+}
+
+// Test all supported LoadReg mnemonics.
+#[test]
+fn test_load_reg() {
+    assert_eq!(asm("ldxw r1, [r2+3]
+                    ldxh r1, [r2+3]
+                    ldxb r1, [r2+3]
+                    ldxdw r1, [r2+3]"),
+               Ok(vec![insn(ebpf::LD_W_REG, 1, 2, 3, 0),
+                       insn(ebpf::LD_H_REG, 1, 2, 3, 0),
+                       insn(ebpf::LD_B_REG, 1, 2, 3, 0),
+                       insn(ebpf::LD_DW_REG, 1, 2, 3, 0)]));
+}
+
+// Test all supported StoreImm mnemonics.
+#[test]
+fn test_store_imm() {
+    assert_eq!(asm("stw [r1+2], 3
+                    sth [r1+2], 3
+                    stb [r1+2], 3
+                    stdw [r1+2], 3"),
+               Ok(vec![insn(ebpf::ST_W_IMM, 1, 0, 2, 3),
+                       insn(ebpf::ST_H_IMM, 1, 0, 2, 3),
+                       insn(ebpf::ST_B_IMM, 1, 0, 2, 3),
+                       insn(ebpf::ST_DW_IMM, 1, 0, 2, 3)]));
+}
+
+// Test all supported StoreReg mnemonics.
+#[test]
+fn test_store_reg() {
+    assert_eq!(asm("stxw [r1+2], r3
+                    stxh [r1+2], r3
+                    stxb [r1+2], r3
+                    stxdw [r1+2], r3"),
+               Ok(vec![insn(ebpf::ST_W_REG, 1, 3, 2, 0),
+                       insn(ebpf::ST_H_REG, 1, 3, 2, 0),
+                       insn(ebpf::ST_B_REG, 1, 3, 2, 0),
+                       insn(ebpf::ST_DW_REG, 1, 3, 2, 0)]));
+}
+
+// Test all supported JumpConditional mnemonics.
+#[test]
+fn test_jump_conditional() {
+    assert_eq!(asm("jeq r1, r2, +3
+                    jgt r1, r2, +3
+                    jge r1, r2, +3
+                    jset r1, r2, +3
+                    jne r1, r2, +3
+                    jsgt r1, r2, +3
+                    jsge r1, r2, +3"),
+               Ok(vec![insn(ebpf::JEQ_REG, 1, 2, 3, 0),
+                       insn(ebpf::JGT_REG, 1, 2, 3, 0),
+                       insn(ebpf::JGE_REG, 1, 2, 3, 0),
+                       insn(ebpf::JSET_REG, 1, 2, 3, 0),
+                       insn(ebpf::JNE_REG, 1, 2, 3, 0),
+                       insn(ebpf::JSGT_REG, 1, 2, 3, 0),
+                       insn(ebpf::JSGE_REG, 1, 2, 3, 0)]));
+
+    assert_eq!(asm("jeq r1, 2, +3
+                    jgt r1, 2, +3
+                    jge r1, 2, +3
+                    jset r1, 2, +3
+                    jne r1, 2, +3
+                    jsgt r1, 2, +3
+                    jsge r1, 2, +3"),
+               Ok(vec![insn(ebpf::JEQ_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JGT_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JGE_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JSET_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JNE_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JSGT_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JSGE_IMM, 1, 0, 3, 2)]));
+}
+
+// Test all supported Endian mnemonics.
+#[test]
+fn test_endian() {
+    assert_eq!(asm("be16 r1
+                    be32 r1
+                    be64 r1
+                    le16 r1
+                    le32 r1
+                    le64 r1"),
+               Ok(vec![insn(ebpf::BE, 1, 0, 0, 16),
+                       insn(ebpf::BE, 1, 0, 0, 32),
+                       insn(ebpf::BE, 1, 0, 0, 64),
+                       insn(ebpf::LE, 1, 0, 0, 16),
+                       insn(ebpf::LE, 1, 0, 0, 32),
+                       insn(ebpf::LE, 1, 0, 0, 64)]));
+}
+
+#[test]
+fn test_large_immediate() {
+    assert_eq!(asm("add64 r1, 2147483647"),
+               Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, 2147483647)]));
+    assert_eq!(asm("add64 r1, -2147483648"),
+               Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, -2147483648)]));
+}
+
+#[test]
+fn test_tcp_sack() {
+    assert_eq!(assemble(&TCP_SACK_ASM), Ok(TCP_SACK_BIN.to_vec()));
+}
+
+#[test]
+fn test_error_invalid_instruction() {
+    assert_eq!(asm("abcd"), Err("Invalid instruction \"abcd\"".to_string()));
+}
+
+#[test]
+fn test_error_unexpected_operands() {
+    assert_eq!(asm("add 1, 2"),
+               Err("Failed to encode add: Unexpected operands: [Integer(1), Integer(2)]"
+                   .to_string()));
+}
+
+#[test]
+fn test_error_too_many_operands() {
+    assert_eq!(asm("add 1, 2, 3, 4"),
+               Err("Failed to encode add: Too many operands".to_string()));
+}
+
+#[test]
+fn test_error_operands_out_of_range() {
+    assert_eq!(asm("add r16, r2"),
+               Err("Failed to encode add: Invalid destination register 16".to_string()));
+    assert_eq!(asm("add r1, r16"),
+               Err("Failed to encode add: Invalid source register 16".to_string()));
+    assert_eq!(asm("ja -32769"),
+               Err("Failed to encode ja: Invalid offset -32769".to_string()));
+    assert_eq!(asm("ja 32768"),
+               Err("Failed to encode ja: Invalid offset 32768".to_string()));
+    assert_eq!(asm("add r1, 4294967296"),
+               Err("Failed to encode add: Invalid immediate 4294967296".to_string()));
+    assert_eq!(asm("add r1, 2147483648"),
+               Err("Failed to encode add: Invalid immediate 2147483648".to_string()));
+    assert_eq!(asm("add r1, -2147483649"),
+               Err("Failed to encode add: Invalid immediate -2147483649".to_string()));
+}

+ 131 - 0
tests/common.rs

@@ -0,0 +1,131 @@
+// 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.
+
+// Assembly code and data for tcp_sack testcases.
+
+#[allow(dead_code)]
+pub const TCP_SACK_ASM: &'static str = "
+    ldxb r2, [r1+12]
+    ldxb r3, [r1+13]
+    lsh r3, 0x8
+    or r3, r2
+    mov r0, 0x0
+    jne r3, 0x8, +37
+    ldxb r2, [r1+23]
+    jne r2, 0x6, +35
+    ldxb r2, [r1+14]
+    add r1, 0xe
+    and r2, 0xf
+    lsh r2, 0x2
+    add r1, r2
+    mov r0, 0x0
+    ldxh r4, [r1+12]
+    add r1, 0x14
+    rsh r4, 0x2
+    and r4, 0x3c
+    mov r2, r4
+    add r2, -20
+    mov r5, 0x15
+    mov r3, 0x0
+    jgt r5, r4, +20
+    mov r5, r3
+    lsh r5, 0x20
+    arsh r5, 0x20
+    mov r4, r1
+    add r4, r5
+    ldxb r5, [r4]
+    jeq r5, 0x1, +4
+    jeq r5, 0x0, +12
+    mov r6, r3
+    jeq r5, 0x5, +9
+    ja +2
+    add r3, 0x1
+    mov r6, r3
+    ldxb r3, [r4+1]
+    add r3, r6
+    lsh r3, 0x20
+    arsh r3, 0x20
+    jsgt r2, r3, -18
+    ja +1
+    mov r0, 0x1
+    exit";
+
+#[allow(dead_code)]
+pub const TCP_SACK_BIN: [u8;352] = [
+    0x71, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x71, 0x13, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+    0x4f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x55, 0x03, 0x25, 0x00, 0x08, 0x00, 0x00, 0x00,
+    0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x55, 0x02, 0x23, 0x00, 0x06, 0x00, 0x00, 0x00,
+    0x71, 0x12, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x07, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+    0x57, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+    0x67, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+    0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x69, 0x14, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x07, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+    0x77, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+    0x57, 0x04, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+    0xbf, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x07, 0x02, 0x00, 0x00, 0xec, 0xff, 0xff, 0xff,
+    0xb7, 0x05, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
+    0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x2d, 0x45, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xbf, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x67, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+    0xc7, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+    0xbf, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x0f, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x71, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x15, 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x15, 0x05, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xbf, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x15, 0x05, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00,
+    0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x07, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0xbf, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x71, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x0f, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x67, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+    0xc7, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+    0x6d, 0x32, 0xee, 0xff, 0x00, 0x00, 0x00, 0x00,
+    0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+    0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+];
+
+#[allow(dead_code)]
+pub const TCP_SACK_MATCH: [u8;78] = [
+    0x00, 0x26, 0x62, 0x2f, 0x47, 0x87, 0x00, 0x1d,
+    0x60, 0xb3, 0x01, 0x84, 0x08, 0x00, 0x45, 0x00,
+    0x00, 0x40, 0xa8, 0xde, 0x40, 0x00, 0x40, 0x06,
+    0x9d, 0x58, 0xc0, 0xa8, 0x01, 0x03, 0x3f, 0x74,
+    0xf3, 0x61, 0xe5, 0xc0, 0x00, 0x50, 0xe5, 0x94,
+    0x3f, 0x77, 0xa3, 0xc4, 0xc4, 0x80, 0xb0, 0x10,
+    0x01, 0x3e, 0x34, 0xb6, 0x00, 0x00, 0x01, 0x01,
+    0x08, 0x0a, 0x00, 0x17, 0x95, 0x6f, 0x8d, 0x9d,
+    0x9e, 0x27, 0x01, 0x01, 0x05, 0x0a, 0xa3, 0xc4,
+    0xca, 0x28, 0xa3, 0xc4, 0xcf, 0xd0
+];
+
+#[allow(dead_code)]
+pub const TCP_SACK_NOMATCH: [u8;66] = [
+    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
+];

+ 11 - 76
tests/ubpf_jit_x86_64.rs

@@ -22,8 +22,11 @@
 
 
 extern crate rbpf;
+mod common;
 
 use rbpf::helpers;
+use rbpf::assembler::assemble;
+use common::{TCP_SACK_ASM, TCP_SACK_MATCH, TCP_SACK_NOMATCH};
 
 #[test]
 fn test_jit_add() {
@@ -1785,88 +1788,20 @@ fn test_jit_tcp_port80_nomatch_proto() {
     unsafe { assert_eq!(vm.prog_exec_jit(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 mem = &mut [
-        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;
-    let mut vm = rbpf::EbpfVmRaw::new(prog);
+    let mut mem = TCP_SACK_MATCH.to_vec();
+    let prog = assemble(TCP_SACK_ASM).unwrap();
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
     vm.jit_compile();
-    unsafe { assert_eq!(vm.prog_exec_jit(mem), 0x1); }
+    unsafe { assert_eq!(vm.prog_exec_jit(mem.as_mut_slice()), 0x1); }
 }
 
 #[test]
 fn test_jit_tcp_sack_nomatch() {
-    let mem = &mut [
-        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;
-    let mut vm = rbpf::EbpfVmRaw::new(prog);
+    let mut mem = TCP_SACK_NOMATCH.to_vec();
+    let prog = assemble(TCP_SACK_ASM).unwrap();
+    let mut vm = rbpf::EbpfVmRaw::new(&prog);
     vm.jit_compile();
-    unsafe { assert_eq!(vm.prog_exec_jit(mem), 0x0); }
+    unsafe { assert_eq!(vm.prog_exec_jit(mem.as_mut_slice()), 0x0); }
 }

+ 14 - 82
tests/ubpf_vm.rs

@@ -22,8 +22,11 @@
 
 
 extern crate rbpf;
+mod common;
 
 use rbpf::helpers;
+use rbpf::assembler::assemble;
+use common::{TCP_SACK_ASM, TCP_SACK_MATCH, TCP_SACK_NOMATCH};
 
 #[test]
 fn test_vm_add() {
@@ -731,12 +734,9 @@ fn test_vm_jsgt_reg() {
 
 #[test]
 fn test_vm_lddw() {
-    let prog = &[
-        0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55,
-        0x00, 0x00, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11,
-        0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-    ];
-    let vm = rbpf::EbpfVmNoData::new(prog);
+    let prog = assemble("lddw r0, 0x1122334455667788
+                         exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
     assert_eq!(vm.prog_exec(), 0x1122334455667788);
 }
 
@@ -1686,86 +1686,18 @@ fn test_vm_tcp_port80_nomatch_proto() {
     assert_eq!(vm.prog_exec(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 mem = &mut [
-        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;
-    let vm = rbpf::EbpfVmRaw::new(prog);
-    assert_eq!(vm.prog_exec(mem), 0x1);
+    let mut mem = TCP_SACK_MATCH.to_vec();
+    let prog = assemble(TCP_SACK_ASM).unwrap();
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(mem.as_mut_slice()), 0x1);
 }
 
 #[test]
 fn test_vm_tcp_sack_nomatch() {
-    let mem = &mut [
-        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;
-    let vm = rbpf::EbpfVmRaw::new(prog);
-    assert_eq!(vm.prog_exec(mem), 0x0);
+    let mut mem = TCP_SACK_NOMATCH.to_vec();
+    let prog = assemble(TCP_SACK_ASM).unwrap();
+    let vm = rbpf::EbpfVmRaw::new(&prog);
+    assert_eq!(vm.prog_exec(mem.as_mut_slice()), 0x0);
 }