From ce4824573040db91a737cf60416a7ff1f04225e3 Mon Sep 17 00:00:00 2001 From: SzymonKubica Date: Wed, 29 May 2024 23:34:09 +0100 Subject: [PATCH] Add lib module with conditional imports depending on std/no_std. Signed-off-by: SzymonKubica Add correct imports for cranelift feature. Signed-off-by: SzymonKubica Add optional loading of dependencies available only when under std. Signed-off-by: SzymonKubica Reenabled the default features on dev dependencies and reverted formatting changes in Cargo.toml Signed-off-by: SzymonKubica --- Cargo.toml | 13 ++- README.md | 57 ++++++----- examples/disassemble.rs | 59 ++++++----- examples/load_elf.rs | 163 ++++++++++++++--------------- examples/uptime.rs | 104 +++++++++---------- src/asm_parser.rs | 9 +- src/assembler.rs | 8 +- src/cranelift.rs | 9 +- src/disassembler.rs | 5 +- src/ebpf.rs | 1 + src/helpers.rs | 10 +- src/insn_builder.rs | 1 + src/interpreter.rs | 4 +- src/lib.rs | 221 ++++++++++++++++++++++++++-------------- src/no_std_error.rs | 35 +++++++ src/verifier.rs | 2 +- tests/assembler.rs | 2 + tests/disassembler.rs | 3 + tests/misc.rs | 7 +- tests/ubpf_verifier.rs | 4 + tests/ubpf_vm.rs | 2 + 21 files changed, 437 insertions(+), 282 deletions(-) create mode 100644 src/no_std_error.rs diff --git a/Cargo.toml b/Cargo.toml index 830619184..9e5a484f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,10 +24,13 @@ include = [ [dependencies] -combine = "4.6" -libc = "0.2" -time = "0.2" -byteorder = "1.2" +byteorder = { version = "1.2", default-features = false } +log = {version = "0.4.21", default-features = false } + +# The following dependencies are only used when `std` is enabled. +combine = { version = "4.6", optional = true } +libc = { version = "0.2", optional = true } +time = { version = "0.2", optional = true } # Optional Dependencies for the CraneLift JIT cranelift-codegen = { version = "0.99", optional = true } @@ -44,7 +47,7 @@ hex = "0.4.3" [features] default = ["std"] -std = [] +std = ["dep:time", "dep:libc", "dep:combine"] cranelift = [ "dep:cranelift-codegen", "dep:cranelift-frontend", diff --git a/README.md b/README.md index 64e2f0365..26de6c353 100644 --- a/README.md +++ b/README.md @@ -447,9 +447,12 @@ fn main() { let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); // We register a helper function, that can be called by the program, into - // the VM. - vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, - helpers::bpf_trace_printf).unwrap(); + // the VM. The `bpf_trace_printf` is only available when we have access to + // the standard library. + #[cfg(feature = "std")] { + vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, + helpers::bpf_trace_printf).unwrap(); + } // This kind of VM takes a reference to the packet data, but does not need // any reference to the metadata buffer: a fixed buffer is handled @@ -469,17 +472,21 @@ methods are available. The first method consists in using the assembler provided by the crate. ```rust -extern crate rbpf; -use rbpf::assembler::assemble; -let prog = assemble("add64 r1, 0x605 - mov64 r2, 0x32 - mov64 r1, r0 - be16 r0 - neg64 r2 - exit").unwrap(); - -println!("{:?}", prog); +extern crate rbpf; +// These tests should only be run on std as they depend on the assembler. +#[cfg(feature = "std")] { + use rbpf::assembler::assemble; + + let prog = assemble("add64 r1, 0x605 + mov64 r2, 0x32 + mov64 r1, r0 + be16 r0 + neg64 r2 + exit").unwrap(); + + println!("{:?}", prog); +} ``` The above snippet will produce: @@ -497,19 +504,21 @@ Conversely, a disassembler is also available to dump instruction names from bytecode in a human-friendly format. ```rust -extern crate rbpf; -use rbpf::disassembler::disassemble; +#[cfg(feature = "std")] { + extern crate rbpf; + use rbpf::disassembler::disassemble; -let prog = &[ - 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, - 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -]; + let prog = &[ + 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, + 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]; -disassemble(prog); + disassemble(prog); +} ``` This will produce the following output: diff --git a/examples/disassemble.rs b/examples/disassemble.rs index e475ecb25..761916008 100644 --- a/examples/disassemble.rs +++ b/examples/disassemble.rs @@ -1,37 +1,40 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) // Copyright 2017 6WIND S.A. + extern crate rbpf; use rbpf::disassembler; // Simply disassemble a program into human-readable instructions. fn main() { - let prog = &[ - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x79, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x79, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x2d, 0x23, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x69, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x02, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x79, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbf, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x57, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, - 0x15, 0x02, 0x08, 0x00, 0x99, 0x99, 0x00, 0x00, - 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0x99, 0x99, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ]; - disassembler::disassemble(prog); + #[cfg(feature = "std")] { + let prog = &[ + 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x79, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x79, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, + 0x2d, 0x23, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x69, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0x02, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x79, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbf, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x57, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x15, 0x02, 0x08, 0x00, 0x99, 0x99, 0x00, 0x00, + 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0x99, 0x99, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]; + disassembler::disassemble(prog); + } } diff --git a/examples/load_elf.rs b/examples/load_elf.rs index 683152f3a..8d90e1ed4 100644 --- a/examples/load_elf.rs +++ b/examples/load_elf.rs @@ -49,85 +49,88 @@ use rbpf::helpers; fn main() { - let filename = "examples/load_elf__block_a_port.o"; - - let path = PathBuf::from(filename); - let file = match elf::File::open_path(path) { - Ok(f) => f, - Err(e) => panic!("Error: {:?}", e), - }; - - let text_scn = match file.get_section(".classifier") { - Some(s) => s, - None => panic!("Failed to look up .classifier section"), - }; - - let prog = &text_scn.data; - - let packet1 = &mut [ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, - 0x08, 0x00, // ethertype - 0x45, 0x00, 0x00, 0x3b, // start ip_hdr - 0xa6, 0xab, 0x40, 0x00, - 0x40, 0x06, 0x96, 0x0f, - 0x7f, 0x00, 0x00, 0x01, - 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 packet2 = &mut [ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, - 0x08, 0x00, // ethertype - 0x45, 0x00, 0x00, 0x3b, // start ip_hdr - 0xa6, 0xab, 0x40, 0x00, - 0x40, 0x06, 0x96, 0x0f, - 0x7f, 0x00, 0x00, 0x01, - 0x7f, 0x00, 0x00, 0x01, - // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0. - 0x98, 0x76, 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(Some(prog), 0x40, 0x50).unwrap(); - vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helpers::bpf_trace_printf).unwrap(); - - let res = vm.execute_program(packet1).unwrap(); - println!("Packet #1, program returned: {res:?} ({res:#x})"); - assert_eq!(res, 0xffffffff); - - #[cfg(all(not(windows), feature = "std"))] - { - vm.jit_compile().unwrap(); - - let res = unsafe { vm.execute_program_jit(packet2).unwrap() }; - println!("Packet #2, program returned: {res:?} ({res:#x})"); - assert_eq!(res, 0); - } - - #[cfg(any(windows, not(feature = "std")))] - { - let res = vm.execute_program(packet2).unwrap(); - println!("Packet #2, program returned: {:?} ({:#x})", res, res); - assert_eq!(res, 0); + // This example relies on the bpf_trace_printf which is only available on std + #[cfg(feature = "std")] { + let filename = "examples/load_elf__block_a_port.o"; + + let path = PathBuf::from(filename); + let file = match elf::File::open_path(path) { + Ok(f) => f, + Err(e) => panic!("Error: {:?}", e), + }; + + let text_scn = match file.get_section(".classifier") { + Some(s) => s, + None => panic!("Failed to look up .classifier section"), + }; + + let prog = &text_scn.data; + + let packet1 = &mut [ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, + 0x08, 0x00, // ethertype + 0x45, 0x00, 0x00, 0x3b, // start ip_hdr + 0xa6, 0xab, 0x40, 0x00, + 0x40, 0x06, 0x96, 0x0f, + 0x7f, 0x00, 0x00, 0x01, + 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 packet2 = &mut [ + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, + 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, + 0x08, 0x00, // ethertype + 0x45, 0x00, 0x00, 0x3b, // start ip_hdr + 0xa6, 0xab, 0x40, 0x00, + 0x40, 0x06, 0x96, 0x0f, + 0x7f, 0x00, 0x00, 0x01, + 0x7f, 0x00, 0x00, 0x01, + // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0. + 0x98, 0x76, 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(Some(prog), 0x40, 0x50).unwrap(); + vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helpers::bpf_trace_printf).unwrap(); + + let res = vm.execute_program(packet1).unwrap(); + println!("Packet #1, program returned: {res:?} ({res:#x})"); + assert_eq!(res, 0xffffffff); + + #[cfg(not(windows))] + { + vm.jit_compile().unwrap(); + + let res = unsafe { vm.execute_program_jit(packet2).unwrap() }; + println!("Packet #2, program returned: {res:?} ({res:#x})"); + assert_eq!(res, 0); + } + + #[cfg(windows)] + { + let res = vm.execute_program(packet2).unwrap(); + println!("Packet #2, program returned: {:?} ({:#x})", res, res); + assert_eq!(res, 0); + } } } diff --git a/examples/uptime.rs b/examples/uptime.rs index 7128ead23..8d31ccba6 100644 --- a/examples/uptime.rs +++ b/examples/uptime.rs @@ -11,65 +11,67 @@ use rbpf::helpers; // // The two eBPF programs are independent and are not related to one another. fn main() { - let prog1 = &[ - 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 and return r0 - ]; + #[cfg(feature = "std")] { + let prog1 = &[ + 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 and return r0 + ]; - // We use helper `bpf_time_getns()`, which is similar to helper `bpf_ktime_getns()` from Linux - // kernel. Hence rbpf::helpers module provides the index of this in-kernel helper as a - // constant, so that we can remain compatible with programs for the kernel. Here we also cast - // it to a u8 so as to use it directly in program instructions. - let hkey = helpers::BPF_KTIME_GETNS_IDX as u8; - let prog2 = &[ - 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0x85, 0x00, 0x00, 0x00, hkey, 0x00, 0x00, 0x00, // call helper - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit and return r0 - ]; + // We use helper `bpf_time_getns()`, which is similar to helper `bpf_ktime_getns()` from Linux + // kernel. Hence rbpf::helpers module provides the index of this in-kernel helper as a + // constant, so that we can remain compatible with programs for the kernel. Here we also cast + // it to a u8 so as to use it directly in program instructions. + let hkey = helpers::BPF_KTIME_GETNS_IDX as u8; + let prog2 = &[ + 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 + 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 + 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 + 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 + 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 + 0x85, 0x00, 0x00, 0x00, hkey, 0x00, 0x00, 0x00, // call helper + 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit and return r0 + ]; - // Create a VM: this one takes no data. Load prog1 in it. - let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap(); - // Execute prog1. - assert_eq!(vm.execute_program().unwrap(), 0x3); + // Create a VM: this one takes no data. Load prog1 in it. + let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap(); + // Execute prog1. + assert_eq!(vm.execute_program().unwrap(), 0x3); - // As struct EbpfVmNoData does not takes any memory area, its return value is mostly - // deterministic. So we know prog1 will always return 3. There is an exception: when it uses - // helpers, the latter may have non-deterministic values, and all calls may not return the same - // value. - // - // In the following example we use a helper to get the elapsed time since boot time: we - // reimplement uptime in eBPF, in Rust. Because why not. + // As struct EbpfVmNoData does not takes any memory area, its return value is mostly + // deterministic. So we know prog1 will always return 3. There is an exception: when it uses + // helpers, the latter may have non-deterministic values, and all calls may not return the same + // value. + // + // In the following example we use a helper to get the elapsed time since boot time: we + // reimplement uptime in eBPF, in Rust. Because why not. - vm.set_program(prog2).unwrap(); - vm.register_helper(helpers::BPF_KTIME_GETNS_IDX, helpers::bpf_time_getns).unwrap(); + vm.set_program(prog2).unwrap(); + vm.register_helper(helpers::BPF_KTIME_GETNS_IDX, helpers::bpf_time_getns).unwrap(); - let time; + let time; - #[cfg(all(not(windows), feature = "std"))] - { - vm.jit_compile().unwrap(); + #[cfg(all(not(windows), feature = "std"))] + { + vm.jit_compile().unwrap(); - time = unsafe { vm.execute_program_jit().unwrap() }; - } + time = unsafe { vm.execute_program_jit().unwrap() }; + } - #[cfg(any(windows, not(feature = "std")))] - { - time = vm.execute_program().unwrap(); - } + #[cfg(any(windows, not(feature = "std")))] + { + time = vm.execute_program().unwrap(); + } - let days = time / 10u64.pow(9) / 60 / 60 / 24; - let hours = (time / 10u64.pow(9) / 60 / 60) % 24; - let minutes = (time / 10u64.pow(9) / 60 ) % 60; - let seconds = (time / 10u64.pow(9)) % 60; - let nanosec = time % 10u64.pow(9); + let days = time / 10u64.pow(9) / 60 / 60 / 24; + let hours = (time / 10u64.pow(9) / 60 / 60) % 24; + let minutes = (time / 10u64.pow(9) / 60 ) % 60; + let seconds = (time / 10u64.pow(9)) % 60; + let nanosec = time % 10u64.pow(9); - println!("Uptime: {:#x} ns == {} days {:02}:{:02}:{:02}, {} ns", - time, days, hours, minutes, seconds, nanosec); + println!("Uptime: {:#x} ns == {} days {:02}:{:02}:{:02}, {} ns", + time, days, hours, minutes, seconds, nanosec); + } } diff --git a/src/asm_parser.rs b/src/asm_parser.rs index 6d3a7106a..1360ebd89 100644 --- a/src/asm_parser.rs +++ b/src/asm_parser.rs @@ -4,7 +4,11 @@ // Rust-doc comments were left in the module, but it is no longer publicly exposed from the root // file of the crate. Do not expect to find those comments in the documentation of the crate. -//! This module parses eBPF assembly language source code. +//! This module parses eBPF assembly language source code. It depends on the +//! `combine` crate, which depends on the standard library. Because of this +//! it is only available when the `std` feature is enabled. + +use crate::lib::*; use combine::parser::char::{alpha_num, char, digit, hex_digit, spaces, string}; use combine::stream::position::{self}; @@ -109,6 +113,7 @@ pub fn parse(input: &str) -> Result, String> { #[cfg(test)] mod tests { + use crate::lib::*; use super::{ident, instruction, integer, operand, parse, register, Instruction, Operand}; use combine::Parser; @@ -593,7 +598,7 @@ exit fn test_initial_whitespace() { assert_eq!( parse( - " + " exit" ), Ok(vec![Instruction { diff --git a/src/assembler.rs b/src/assembler.rs index 52bb6dc49..d1eb91146 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -1,12 +1,16 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) // Copyright 2017 Rich Lane -//! This module translates eBPF assembly language to binary. +//! This module translates eBPF assembly language to binary. It depends on the +//! `combine` crate, which depends on the standard library. Because of this +//! it is only available when the `std` feature is enabled. + + +use crate::lib::*; 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}; diff --git a/src/cranelift.rs b/src/cranelift.rs index 7ea269088..c0a8cd7a1 100644 --- a/src/cranelift.rs +++ b/src/cranelift.rs @@ -1,11 +1,6 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) -use std::{ - collections::{BTreeMap, HashMap, HashSet}, - convert::TryInto, - io::ErrorKind, - mem::ManuallyDrop, -}; +use crate::lib::*; use cranelift_codegen::{ entity::EntityRef, @@ -1197,7 +1192,7 @@ impl CraneliftProgram { /// module, since it's not guaranteed to be valid after the module is dropped. pub(crate) fn get_main_function(&self) -> JittedFunction { let function_ptr = self.module.get_finalized_function(self.main_id); - unsafe { std::mem::transmute(function_ptr) } + unsafe { mem::transmute(function_ptr) } } /// Execute this module by calling the main function diff --git a/src/disassembler.rs b/src/disassembler.rs index 41fd09e86..f9dc09d14 100644 --- a/src/disassembler.rs +++ b/src/disassembler.rs @@ -4,7 +4,9 @@ //! Functions in this module are used to handle eBPF programs with a higher level representation, //! for example to disassemble the code into a human-readable format. +use crate::lib::*; use ebpf; +use log::warn; #[inline] fn alu_imm_str(name: &str, insn: &ebpf::Insn) -> String { @@ -20,7 +22,7 @@ fn alu_reg_str(name: &str, insn: &ebpf::Insn) -> String { fn byteswap_str(name: &str, insn: &ebpf::Insn) -> String { match insn.imm { 16 | 32 | 64 => {}, - _ => println!("[Disassembler] Warning: Invalid offset value for {name} insn") + _ => warn!("[Disassembler] Warning: Invalid offset value for {name} insn") } format!("{name}{} r{}", insn.imm, insn.dst) } @@ -383,6 +385,7 @@ pub fn to_insn_vec(prog: &[u8]) -> Vec { /// neg64 r2 /// exit /// ``` +#[cfg(feature = "std")] pub fn disassemble(prog: &[u8]) { for insn in to_insn_vec(prog) { println!("{}", insn.desc); diff --git a/src/ebpf.rs b/src/ebpf.rs index 6f2432d7f..bf42c45c2 100644 --- a/src/ebpf.rs +++ b/src/ebpf.rs @@ -14,6 +14,7 @@ //! , or for a shorter version of //! the list of the operation codes: +use crate::lib::*; use byteorder::{ByteOrder, LittleEndian}; /// Maximum number of instructions in an eBPF program. diff --git a/src/helpers.rs b/src/helpers.rs index 07b972440..cad143b84 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -16,9 +16,10 @@ //! value. Hence some helpers have unused arguments, or return a 0 value in all cases, in order to //! respect this convention. -extern crate libc; +use crate::lib::*; -use std::u64; +#[cfg(feature = "std")] +extern crate libc; // Helpers associated to kernel helpers // See also linux/include/uapi/linux/bpf.h in Linux kernel sources. @@ -47,6 +48,7 @@ pub const BPF_KTIME_GETNS_IDX: u32 = 5; #[allow(dead_code)] #[allow(unused_variables)] #[allow(deprecated)] +#[cfg(feature = "std")] pub fn bpf_time_getns (unused1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 { time::precise_time_ns() } @@ -94,6 +96,7 @@ pub const BPF_TRACE_PRINTK_IDX: u32 = 6; /// program is run. #[allow(dead_code)] #[allow(unused_variables)] +#[cfg(feature = "std")] pub fn bpf_trace_printf (unused1: u64, unused2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 { println!("bpf_trace_printf: {arg3:#x}, {arg4:#x}, {arg5:#x}"); let size_arg = | x | { @@ -191,6 +194,7 @@ pub fn memfrob (ptr: u64, len: u64, unused3: u64, unused4: u64, unused5: u64) -> /// ``` #[allow(dead_code)] #[allow(unused_variables)] +#[cfg(feature = "std")] // sqrt is only available when using `std` pub fn sqrti (arg1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 { (arg1 as f64).sqrt() as u64 } @@ -250,6 +254,7 @@ pub fn strcmp (arg1: u64, arg2: u64, arg3: u64, unused4: u64, unused5: u64) -> u /// extern crate time; /// /// unsafe { +/// /// libc::srand(time::precise_time_ns() as u32) /// } /// @@ -258,6 +263,7 @@ pub fn strcmp (arg1: u64, arg2: u64, arg3: u64, unused4: u64, unused5: u64) -> u /// ``` #[allow(dead_code)] #[allow(unused_variables)] +#[cfg(feature = "std")] pub fn rand (min: u64, max: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 { let mut n = unsafe { (libc::rand() as u64).wrapping_shl(32) + libc::rand() as u64 diff --git a/src/insn_builder.rs b/src/insn_builder.rs index 9a66171e9..d197872ba 100644 --- a/src/insn_builder.rs +++ b/src/insn_builder.rs @@ -3,6 +3,7 @@ //! Module provides API to create eBPF programs by Rust programming language +use crate::lib::*; use ebpf::*; /// Represents single eBPF instruction diff --git a/src/interpreter.rs b/src/interpreter.rs index 31c95a57a..23ff20df0 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -5,8 +5,8 @@ // Copyright 2016 6WIND S.A. // (Translation to Rust, MetaBuff/multiple classes addition, hashmaps for helpers) -use std::collections::HashMap; -use std::io::{Error, ErrorKind}; + +use crate::lib::*; use ebpf; diff --git a/src/lib.rs b/src/lib.rs index 662d3cf73..c76cb86d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,9 +27,17 @@ ) )] +#![cfg_attr(not(feature = "std"), no_std)] + extern crate byteorder; +#[cfg(feature = "std")] extern crate combine; +#[cfg(feature = "std")] extern crate time; +extern crate log; + +#[cfg(not(feature = "std"))] +extern crate alloc; #[cfg(feature = "cranelift")] extern crate cranelift_codegen; @@ -43,11 +51,10 @@ extern crate cranelift_module; extern crate cranelift_native; use byteorder::{ByteOrder, LittleEndian}; -use std::collections::HashMap; -use std::io::{Error, ErrorKind}; -use std::u32; +#[cfg(feature = "std")] mod asm_parser; +#[cfg(feature = "std")] pub mod assembler; #[cfg(feature = "cranelift")] mod cranelift; @@ -59,6 +66,63 @@ mod interpreter; #[cfg(all(not(windows), feature = "std"))] mod jit; mod verifier; +#[cfg(not(feature = "std"))] +mod no_std_error; + +/// A facade around all the types needed from the `std`, `core`, and `alloc` +/// crates. This avoids elaborate import wrangling having to happen in every +/// module. Inspired by the design used by `serde`. +pub mod lib { + mod core { + #[cfg(not(feature = "std"))] + pub use core::*; + #[cfg(feature = "std")] + pub use std::*; + } + + pub use self::core::convert::TryInto; + pub use self::core::mem; + pub use self::core::mem::ManuallyDrop; + pub use self::core::ptr; + + pub use self::core::{u32, u64, f64}; + + #[cfg(feature = "std")] + pub use std::println; + + #[cfg(not(feature = "std"))] + pub use alloc::vec; + #[cfg(not(feature = "std"))] + pub use alloc::vec::Vec; + #[cfg(feature = "std")] + pub use std::vec::Vec; + + #[cfg(not(feature = "std"))] + pub use alloc::string::{String, ToString}; + #[cfg(feature = "std")] + pub use std::string::{String, ToString}; + + + // In no_std we cannot use randomness for hashing, thus we need to use + // BTree-based implementations of Maps and Sets. The cranelift module uses + // BTrees by default, hence we need to expose it twice here. + #[cfg(not(feature = "std"))] + pub use alloc::collections::{BTreeMap as HashMap, BTreeMap, BTreeSet as HashSet, BTreeSet}; + #[cfg(feature = "std")] + pub use std::collections::{BTreeMap, HashMap, HashSet}; + + /// In no_std we use a custom implementation of the error which acts as a + /// replacement for the io Error. + #[cfg(not(feature = "std"))] + pub use crate::no_std_error::{Error, ErrorKind}; + #[cfg(feature = "std")] + pub use std::io::{Error, ErrorKind}; + + #[cfg(not(feature = "std"))] + pub use alloc::format; +} + +use crate::lib::*; /// eBPF verification function that returns an error if the program does not meet its requirements. /// @@ -189,7 +253,7 @@ impl<'a> EbpfVmMbuff<'a> { /// # Examples /// /// ``` - /// use std::io::{Error, ErrorKind}; + /// use rbpf::lib::{Error, ErrorKind}; /// use rbpf::ebpf; /// /// // Define a simple verifier function. @@ -253,6 +317,7 @@ impl<'a> EbpfVmMbuff<'a> { /// // Register a helper. /// // On running the program this helper will print the content of registers r3, r4 and r5 to /// // standard output. + /// # #[cfg(feature = "std")] /// vm.register_helper(6, helpers::bpf_trace_printf).unwrap(); /// ``` pub fn register_helper(&mut self, key: u32, function: Helper) -> Result<(), Error> { @@ -506,7 +571,7 @@ impl<'a> EbpfVmMbuff<'a> { // in the kernel; anyway the verifier would prevent the use of uninitialized registers). // See `mul_loop` test. let mem_ptr = match mem.len() { - 0 => std::ptr::null_mut(), + 0 => ptr::null_mut(), _ => mem.as_ptr() as *mut u8, }; @@ -687,7 +752,7 @@ impl<'a> EbpfVmFixedMbuff<'a> { /// # Examples /// /// ``` - /// use std::io::{Error, ErrorKind}; + /// use rbpf::lib::{Error, ErrorKind}; /// use rbpf::ebpf; /// /// // Define a simple verifier function. @@ -724,37 +789,39 @@ impl<'a> EbpfVmFixedMbuff<'a> { /// # Examples /// /// ``` - /// use rbpf::helpers; - /// - /// // This program was compiled with clang, from a C program containing the following single - /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 6 instructions - /// 0x71, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r1 - /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 - /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 - /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 - /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 - /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x09, - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - /// - /// // Register a helper. This helper will store the result of the square root of r1 into r0. - /// vm.register_helper(1, helpers::sqrti); - /// - /// let res = vm.execute_program(mem).unwrap(); - /// assert_eq!(res, 3); + /// #[cfg(feature = "std")] { + /// use rbpf::helpers; + /// + /// // This program was compiled with clang, from a C program containing the following single + /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);` + /// let prog = &[ + /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 + /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 + /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 + /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 + /// 0x2d, 0x12, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 6 instructions + /// 0x71, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r1 + /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 + /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 + /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 + /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 + /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 + /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit + /// ]; + /// + /// let mem = &mut [ + /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x09, + /// ]; + /// + /// // Instantiate a VM. + /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); + /// + /// // Register a helper. This helper will store the result of the square root of r1 into r0. + /// vm.register_helper(1, helpers::sqrti); + /// + /// let res = vm.execute_program(mem).unwrap(); + /// assert_eq!(res, 3); + /// } /// ``` pub fn register_helper( &mut self, @@ -903,7 +970,7 @@ impl<'a> EbpfVmFixedMbuff<'a> { // in the kernel; anyway the verifier would prevent the use of uninitialized registers). // See `mul_loop` test. let mem_ptr = match mem.len() { - 0 => std::ptr::null_mut(), + 0 => ptr::null_mut(), _ => mem.as_ptr() as *mut u8, }; @@ -1005,7 +1072,7 @@ impl<'a> EbpfVmFixedMbuff<'a> { // in the kernel; anyway the verifier would prevent the use of uninitialized registers). // See `mul_loop` test. let mem_ptr = match mem.len() { - 0 => std::ptr::null_mut(), + 0 => ptr::null_mut(), _ => mem.as_ptr() as *mut u8, }; @@ -1126,7 +1193,7 @@ impl<'a> EbpfVmRaw<'a> { /// # Examples /// /// ``` - /// use std::io::{Error, ErrorKind}; + /// use rbpf::lib::{Error, ErrorKind}; /// use rbpf::ebpf; /// /// // Define a simple verifier function. @@ -1163,30 +1230,32 @@ impl<'a> EbpfVmRaw<'a> { /// # Examples /// /// ``` - /// use rbpf::helpers; + /// #[cfg(feature = "std")] { + /// use rbpf::helpers; /// - /// let prog = &[ - /// 0x79, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxdw r1, r1[0x00] - /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 - /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 - /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 - /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 - /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; + /// let prog = &[ + /// 0x79, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxdw r1, r1[0x00] + /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 + /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 + /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 + /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 + /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 + /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit + /// ]; /// - /// let mem = &mut [ - /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - /// ]; + /// let mem = &mut [ + /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + /// ]; /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); + /// // Instantiate a VM. + /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); /// - /// // Register a helper. This helper will store the result of the square root of r1 into r0. - /// vm.register_helper(1, helpers::sqrti); + /// // Register a helper. This helper will store the result of the square root of r1 into r0. + /// vm.register_helper(1, helpers::sqrti); /// - /// let res = vm.execute_program(mem).unwrap(); - /// assert_eq!(res, 0x10000000); + /// let res = vm.execute_program(mem).unwrap(); + /// assert_eq!(res, 0x10000000); + /// } /// ``` pub fn register_helper( &mut self, @@ -1471,7 +1540,7 @@ impl<'a> EbpfVmNoData<'a> { /// # Examples /// /// ``` - /// use std::io::{Error, ErrorKind}; + /// use rbpf::lib::{Error, ErrorKind}; /// use rbpf::ebpf; /// /// // Define a simple verifier function. @@ -1508,25 +1577,27 @@ impl<'a> EbpfVmNoData<'a> { /// # Examples /// /// ``` - /// use rbpf::helpers; + /// #[cfg(feature = "std")] { + /// use rbpf::helpers; /// - /// let prog = &[ - /// 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // mov r1, 0x010000000 - /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 - /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 - /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 - /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 - /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; + /// let prog = &[ + /// 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // mov r1, 0x010000000 + /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 + /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 + /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 + /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 + /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 + /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit + /// ]; /// - /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); + /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); /// - /// // Register a helper. This helper will store the result of the square root of r1 into r0. - /// vm.register_helper(1, helpers::sqrti).unwrap(); + /// // Register a helper. This helper will store the result of the square root of r1 into r0. + /// vm.register_helper(1, helpers::sqrti).unwrap(); /// - /// let res = vm.execute_program().unwrap(); - /// assert_eq!(res, 0x1000); + /// let res = vm.execute_program().unwrap(); + /// assert_eq!(res, 0x1000); + /// } /// ``` pub fn register_helper( &mut self, diff --git a/src/no_std_error.rs b/src/no_std_error.rs new file mode 100644 index 000000000..03979f6b6 --- /dev/null +++ b/src/no_std_error.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: (Apache-2.0 OR MIT) + +//! This module provides a simple implementation of the Error struct that is +//! used as a drop-in replacement for std::io::Error when using rbpf in no_std. + +use crate::lib::String; +/// Dummy implementation of Error for no_std applications. +/// It ensures that the existing code can use it with the same interface +/// as the Error from std::io::Error. +#[derive(Debug)] +pub struct Error { + #[allow(dead_code)] + kind: ErrorKind, + #[allow(dead_code)] + error: String, +} + +impl Error { + /// New function added for compatibility with the existing code. + pub fn new>(kind: ErrorKind, error: S) -> Error { + Error { + kind, + error: error.into(), + } + } +} + +/// The minimum set of variants to make the dummy ErrorKind work with +/// the existing code. +#[derive(Debug)] +pub enum ErrorKind { + /// The no_std code only uses this variant. + Other, +} + diff --git a/src/verifier.rs b/src/verifier.rs index c623038ff..0e5cc5641 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -19,8 +19,8 @@ // Contrary to the verifier of the Linux kernel, this one does not modify the bytecode at all. +use crate::lib::*; use ebpf; -use std::io::{Error, ErrorKind}; fn reject>(msg: S) -> Result<(), Error> { let full_msg = format!("[Verifier] Error: {}", msg.as_ref()); diff --git a/tests/assembler.rs b/tests/assembler.rs index 0fd76c283..055942006 100644 --- a/tests/assembler.rs +++ b/tests/assembler.rs @@ -1,6 +1,8 @@ // SPDX-License-Identifier: (Apache-2.0 OR MIT) // Copyright 2017 Rich Lane +// These tests should only be run on std as they depend on the assembler. +#![cfg(feature = "std")] #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] extern crate rbpf; diff --git a/tests/disassembler.rs b/tests/disassembler.rs index 3d1503b69..e1cdfb2c6 100644 --- a/tests/disassembler.rs +++ b/tests/disassembler.rs @@ -3,6 +3,9 @@ // // Adopted from tests in `tests/assembler.rs` +// These tests should only be run on std as they depend on the assembler. +#![cfg(feature = "std")] + extern crate rbpf; mod common; diff --git a/tests/misc.rs b/tests/misc.rs index 889da3296..685b031b6 100644 --- a/tests/misc.rs +++ b/tests/misc.rs @@ -17,9 +17,11 @@ // extern crate elf; // use std::path::PathBuf; -extern crate rbpf; +// These tests should only be run on std as they depend on the assembler. +#![cfg(feature = "std")] -use std::io::{Error, ErrorKind}; +extern crate rbpf; +use rbpf::lib::{Error, ErrorKind}; use rbpf::assembler::assemble; use rbpf::helpers; @@ -90,6 +92,7 @@ use rbpf::helpers; // instead. #[test] +#[cfg(feature = "std")] 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 diff --git a/tests/ubpf_verifier.rs b/tests/ubpf_verifier.rs index 09227aa12..01e0bdb77 100644 --- a/tests/ubpf_verifier.rs +++ b/tests/ubpf_verifier.rs @@ -16,11 +16,15 @@ // These are unit tests for the eBPF “verifier”. +// These tests should only be run on std as they depend on the assembler. +#![cfg(feature = "std")] + extern crate rbpf; use rbpf::assembler::assemble; use rbpf::ebpf; + #[test] #[should_panic(expected = "[Verifier] Error: unsupported argument for LE/BE (insn #0)")] fn test_verifier_err_endian_size() { diff --git a/tests/ubpf_vm.rs b/tests/ubpf_vm.rs index cdb0943fc..2da27a64d 100644 --- a/tests/ubpf_vm.rs +++ b/tests/ubpf_vm.rs @@ -16,6 +16,8 @@ // These are unit tests for the eBPF interpreter. +// These tests should only be run on std as they depend on the assembler. +#![cfg(feature = "std")] #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] extern crate rbpf;