mod formats;
mod optimizer;
pub mod compiler;
pub mod error;
pub mod types;
#[cfg(test)]
mod tests {
use crate::compiler::Compiler;
use crate::error::Result;
use crate::types::{AddToTypeDatabase, Field, TypeDatabase};
use bpf_ins::{Instruction, Register};
#[repr(C, align(1))]
struct LargeType {
pub a: u64,
pub b: u32,
pub c: u16,
pub d: u8,
}
impl AddToTypeDatabase for LargeType {
fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
let a_id = u64::add_to_database(database)?;
let b_id = u32::add_to_database(database)?;
let c_id = u16::add_to_database(database)?;
let d_id = u8::add_to_database(database)?;
database.add_struct_by_ids(
Some("LargeType"),
&[("a", a_id), ("b", b_id), ("c", c_id), ("d", d_id)],
)
}
}
fn compile_and_compare(prog: &str, expected: &[Instruction]) {
let mut database = TypeDatabase::default();
LargeType::add_to_database(&mut database).expect("Failed to add type.");
database
.add_integer(Some("int"), 4, true)
.expect("Failed to add type.");
let u64id = database
.add_integer(Some("__u64"), 8, false)
.expect("Failed to add type.");
let iov_base = Field {
offset: 0,
type_id: u64id,
};
let iov_len = Field {
offset: 64,
type_id: u64id,
};
database
.add_struct(
Some("iovec"),
&[("iov_base", iov_base), ("iov_len", iov_len)],
)
.expect("Failed to add type.");
let mut compiler = Compiler::create(&database);
compiler.compile(prog).unwrap();
let instructions = compiler.get_instructions();
assert_eq!(instructions.len(), expected.len());
for (i, ins) in instructions.iter().enumerate() {
assert_eq!(ins, &expected[i]);
}
}
#[test]
fn empty_program() {
let prog = r#"
fn()
"#;
let expected = [
Instruction::mov64(Register::R0, 0), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
#[test]
fn return_immediate() {
let prog = r#"
fn()
return 300
"#;
let expected = [
Instruction::mov64(Register::R0, 300), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
#[test]
fn return_input_value() {
let prog = r#"
fn(a: int)
return a
"#;
let expected = [
Instruction::storex64(Register::R10, -8, Register::R1), Instruction::loadx32(Register::R0, Register::R10, -8), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
#[test]
fn assign_fields() {
let prog = r#"
fn()
vec: iovec = 0
vec.iov_base = 100
vec.iov_len = 200
"#;
let expected = [
Instruction::store64(Register::R10, -16, 0), Instruction::store64(Register::R10, -8, 0), Instruction::store64(Register::R10, -16, 100), Instruction::store64(Register::R10, -8, 200), Instruction::mov64(Register::R0, 0), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
#[test]
fn assign_fields_from_fields() {
let prog = r#"
fn(vec: &iovec)
vec_copy: iovec = 0
vec_copy.iov_base = vec.iov_base
vec_copy.iov_len = vec.iov_len
return 50
"#;
let expected = [
Instruction::storex64(Register::R10, -8, Register::R1), Instruction::store64(Register::R10, -24, 0), Instruction::store64(Register::R10, -16, 0), Instruction::loadx64(Register::R6, Register::R10, -8), Instruction::movx64(Register::R1, Register::R10), Instruction::add64(Register::R1, -24), Instruction::mov64(Register::R2, 8), Instruction::movx64(Register::R3, Register::R6), Instruction::call(4), Instruction::loadx64(Register::R6, Register::R10, -8), Instruction::add64(Register::R6, 8), Instruction::movx64(Register::R1, Register::R10), Instruction::add64(Register::R1, -16), Instruction::mov64(Register::R2, 8), Instruction::movx64(Register::R3, Register::R6), Instruction::call(4), Instruction::mov64(Register::R0, 50), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
#[test]
fn assign_function_call() {
let prog = r#"
fn()
a: __u64 = get_current_uid_gid()
"#;
let expected = [
Instruction::call(15), Instruction::storex64(Register::R10, -8, Register::R0), Instruction::mov64(Register::R0, 0), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
#[test]
fn return_function_call() {
let prog = r#"
fn()
a: __u64 = 100
return get_current_uid_gid()
"#;
let expected = [
Instruction::store64(Register::R10, -8, 100), Instruction::call(15), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
#[test]
fn return_nested_function_call() {
let prog = r#"
fn()
return get_current_uid_gid(get_current_uid_gid())
"#;
let expected = [
Instruction::call(15), Instruction::movx64(Register::R1, Register::R0), Instruction::call(15), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
#[test]
fn test_zero_init() {
let prog = r#"
fn()
type: LargeType = 0
"#;
let expected = [
Instruction::store64(Register::R10, -15, 0), Instruction::store32(Register::R10, -7, 0), Instruction::store16(Register::R10, -3, 0), Instruction::store8(Register::R10, -1, 0), Instruction::mov64(Register::R0, 0), Instruction::exit(), ];
compile_and_compare(prog, &expected);
}
}