Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for BPF arch #144

Merged
merged 4 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 102 additions & 0 deletions capstone-rs/src/arch/bpf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! Contains bpf specific types

use core::convert::From;
use core::{cmp, fmt, slice};

pub use capstone_sys::bpf_insn_group as BpfInsnGroup;
pub use capstone_sys::bpf_insn as BpfInsn;
pub use capstone_sys::bpf_reg as BpfReg;
use capstone_sys::{cs_bpf, cs_bpf_op, bpf_op_mem, bpf_op_type};

pub use crate::arch::arch_builder::bpf::*;
use crate::arch::DetailsArchInsn;
use crate::instruction::{RegId, RegIdInt};

/// Contains BPF-specific details for an instruction
pub struct BpfInsnDetail<'a>(pub(crate) &'a cs_bpf);

impl_PartialEq_repr_fields!(BpfInsnDetail<'a> [ 'a ];
operands
);

/// BPF operand
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum BpfOperand {
/// Register
Reg(RegId),

/// Immediate
Imm(u64),

/// Memory
Mem(BpfOpMem),

/// Offset
Off(u32),

/// Mmem
Mmem(u32),

/// Msh
Msh(u32),

/// Ext
Ext(u32),

/// Invalid
Invalid,
}

impl Default for BpfOperand {
fn default() -> Self {
BpfOperand::Invalid
}
}


/// Bpf memory operand
#[derive(Debug, Copy, Clone)]
pub struct BpfOpMem(pub(crate) bpf_op_mem);

impl BpfOpMem {
/// Base register
pub fn base(&self) -> RegId {
RegId(self.0.base as RegIdInt)
}

/// Disp value
pub fn disp(&self) -> u32 {
self.0.disp
}
}

impl_PartialEq_repr_fields!(BpfOpMem;
base, disp
);

impl cmp::Eq for BpfOpMem {}

impl<'a> From<&'a cs_bpf_op> for BpfOperand {
fn from(insn: &cs_bpf_op) -> BpfOperand {
match insn.type_ {
bpf_op_type::BPF_OP_EXT => BpfOperand::Ext(unsafe { insn.__bindgen_anon_1.ext }),
bpf_op_type::BPF_OP_INVALID => BpfOperand::Invalid,
bpf_op_type::BPF_OP_REG => BpfOperand::Reg(RegId(unsafe {insn.__bindgen_anon_1.reg} as RegIdInt)),
bpf_op_type::BPF_OP_IMM => BpfOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }),
bpf_op_type::BPF_OP_MEM => BpfOperand::Mem(BpfOpMem(unsafe { insn.__bindgen_anon_1.mem})),
bpf_op_type::BPF_OP_OFF => BpfOperand::Off(unsafe { insn.__bindgen_anon_1.off }),
bpf_op_type::BPF_OP_MMEM => BpfOperand::Mmem(unsafe { insn.__bindgen_anon_1.mmem }),
bpf_op_type::BPF_OP_MSH => BpfOperand::Msh(unsafe { insn.__bindgen_anon_1.msh }),
}
}
}

def_arch_details_struct!(
InsnDetail = BpfInsnDetail;
Operand = BpfOperand;
OperandIterator = BpfOperandIterator;
OperandIteratorLife = BpfOperandIterator<'a>;
[ pub struct BpfOperandIterator<'a>(slice::Iter<'a, cs_bpf_op>); ]
cs_arch_op = cs_bpf_op;
cs_arch = cs_bpf;
);
17 changes: 17 additions & 0 deletions capstone-rs/src/arch/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ macro_rules! arch_info_base {
( syntax: )
( both_endian: false )
]
[
( bpf, BPF )
( mode:
Cbpf,
Ebpf,
)
( extra_modes: )
( syntax: )
( both_endian: true )
]
);
};
}
Expand Down Expand Up @@ -534,6 +544,13 @@ macro_rules! detail_arch_base {
/// Returns the XCore details, if any
=> arch_name = xcore,
]
[
detail = BpfDetail,
insn_detail = BpfInsnDetail<'a>,
op = BpfOperand,
/// Returns the XCore details, if any
tmfink marked this conversation as resolved.
Show resolved Hide resolved
=> arch_name = bpf,
]
);
};
}
Expand Down
6 changes: 6 additions & 0 deletions capstone-rs/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ define_cs_enum_wrapper!(
=> EVM = CS_ARCH_EVM;
/// RISC-V
=> RISCV = CS_ARCH_RISCV;
/// BPF
=> BPF = CS_ARCH_BPF;
);

define_cs_enum_wrapper!(
Expand Down Expand Up @@ -286,6 +288,10 @@ define_cs_enum_wrapper!(
=> RiscV32 = CS_MODE_RISCV32;
/// RISC-V 64-bit mode
=> RiscV64 = CS_MODE_RISCV64;
/// Classic BPF mode
=> Cbpf = CS_MODE_BPF_CLASSIC;
/// Extended BPF mode
=> Ebpf = CS_MODE_BPF_EXTENDED;
/// Default mode for little-endian
=> Default = CS_MODE_LITTLE_ENDIAN;
);
Expand Down
1 change: 1 addition & 0 deletions capstone-rs/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ impl<'a> InsnDetail<'a> {
[TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x]
[X86, X86Detail, X86InsnDetail, x86]
[XCORE, XcoreDetail, XcoreInsnDetail, xcore]
[BPF, BpfDetail, BpfInsnDetail, bpf]
);
}
}
Expand Down
107 changes: 107 additions & 0 deletions capstone-rs/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ use super::*;

const X86_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00";
const ARM_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00";
const CBPF_CODE: &[u8] = b"\x94\x09\x00\x00\x37\x13\x03\x00\
\x87\x00\x00\x00\x00\x00\x00\x00\
\x07\x00\x00\x00\x00\x00\x00\x00\
\x16\x00\x00\x00\x00\x00\x00\x00\
\x80\x00\x00\x00\x00\x00\x00\x00";
const EBPF_CODE: &[u8] = b"\x97\x09\x00\x00\x37\x13\x03\x00\
\xdc\x02\x00\x00\x20\x00\x00\x00\
\x30\x00\x00\x00\x00\x00\x00\x00\
\xdb\x3a\x00\x01\x00\x00\x00\x00\
\x84\x02\x00\x00\x00\x00\x00\x00\
\x6d\x33\x17\x02\x00\x00\x00\x00";

// Aliases for group types
const JUMP: cs_group_type::Type = cs_group_type::CS_GRP_JUMP;
Expand Down Expand Up @@ -3244,3 +3255,99 @@ fn test_owned_insn() {
assert_eq!(format!("{:?}", insn), format!("{:?}", owned));
}
}


/// Print register names
fn reg_names(cs: &Capstone, regs: &[RegId]) -> String {
let names: Vec<String> = regs.iter().map(|&x| cs.reg_name(x).unwrap()).collect();
names.join(", ")
}

/// Print instruction group names
fn group_names(cs: &Capstone, regs: &[InsnGroupId]) -> String {
let names: Vec<String> = regs.iter().map(|&x| cs.group_name(x).unwrap()).collect();
names.join(", ")
}

#[test]
fn test_cbpf() {
let cs = Capstone::new().bpf().mode(bpf::ArchMode::Cbpf).endian(Endian::Little).detail(true).build().unwrap();
let insns = cs.disasm_all(CBPF_CODE, 0x1000);
match insns {
Ok(ins) => {
for i in ins.as_ref() {
println!();
eprintln!("{}", i);

let detail: InsnDetail = cs.insn_detail(&i).expect("Failed to get insn detail");
let arch_detail: ArchDetail = detail.arch_detail();
let ops = arch_detail.operands();

let output: &[(&str, String)] = &[
("insn id:", format!("{:?}", i.id().0)),
("bytes:", format!("{:?}", i.bytes())),
("read regs:", reg_names(&cs, detail.regs_read())),
("write regs:", reg_names(&cs, detail.regs_write())),
("insn groups:", group_names(&cs, detail.groups())),
];

for &(ref name, ref message) in output.iter() {
eprintln!("{:4}{:12} {}", "", name, message);
}

println!("{:4}operands: {}", "", ops.len());
for op in ops {
eprintln!("{:8}{:?}", "", op);
}

}
}

Err(e) => {
eprintln!("{:?}", e);
assert!(false);
tmfink marked this conversation as resolved.
Show resolved Hide resolved
}
}

}

fn test_ebpf() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also add a test function test_arch_bpf_detail() that tests the details for each instruction. These test_arch_*_detail() tests catch a lot of bugs and changes in behavior when we upgrade the C library. For an example, see test_arch_x86_detail():

fn test_arch_x86_detail() {

let cs = Capstone::new().bpf().mode(bpf::ArchMode::Ebpf).endian(Endian::Little).detail(true).build().unwrap();
let insns = cs.disasm_all(EBPF_CODE, 0x1000);
match insns {
Ok(ins) => {
for i in ins.as_ref() {
println!();
eprintln!("{}", i);

let detail: InsnDetail = cs.insn_detail(&i).expect("Failed to get insn detail");
let arch_detail: ArchDetail = detail.arch_detail();
let ops = arch_detail.operands();

let output: &[(&str, String)] = &[
("insn id:", format!("{:?}", i.id().0)),
("bytes:", format!("{:?}", i.bytes())),
("read regs:", reg_names(&cs, detail.regs_read())),
("write regs:", reg_names(&cs, detail.regs_write())),
("insn groups:", group_names(&cs, detail.groups())),
];

for &(ref name, ref message) in output.iter() {
eprintln!("{:4}{:12} {}", "", name, message);
}

println!("{:4}operands: {}", "", ops.len());
for op in ops {
eprintln!("{:8}{:?}", "", op);
}

}
}

Err(e) => {
eprintln!("{:?}", e);
assert!(false);
tmfink marked this conversation as resolved.
Show resolved Hide resolved
}
}

}
2 changes: 1 addition & 1 deletion capstone-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ travis-ci = { repository = "capstone-rust/capstone-sys" }
libc = { version = "0.2.59", default-features = false }

[build-dependencies]
bindgen = { optional = true, version = "0.59.1" }
bindgen = { optional = true, version = "0.62.0" }
regex = { optional = true, version = "1.3.1" }
cc = "1.0"

Expand Down
3 changes: 2 additions & 1 deletion capstone-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ fn build_capstone_cc() {
.define("CAPSTONE_HAS_WASM", None)
.define("CAPSTONE_HAS_X86", None)
.define("CAPSTONE_HAS_XCORE", None)
.define("CAPSTONE_HAS_BPF", None)
// No need to display any warnings from the C library
.flag_if_supported("-w")
.static_crt(use_static_crt);
Expand Down Expand Up @@ -282,7 +283,7 @@ fn write_bindgen_bindings(
bindings
.write_to_file(&out_bindings_path)
.expect("Unable to write bindings");

// Parse bindings and impl fn to cast u32 to <arch>_insn, write output to file
let bindings_impl_str = impl_insid_to_insenum(&bindings.to_string());
let mut bindings_impl = File::create(&out_impl_path).expect("Unable to open file");
Expand Down
4 changes: 4 additions & 0 deletions capstone-sys/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ pub static ARCH_INCLUDES: &[CapstoneArchInfo<'static>] = &[
header_name: "xcore.h",
cs_name: "xcore",
},
CapstoneArchInfo {
header_name: "bpf.h",
cs_name: "bpf"
}
];

pub static BINDINGS_FILE: &str = "capstone.rs";
Expand Down