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 all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
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 @@ -163,13 +163,13 @@
impl < $($impl_lifetime),* > $struct < $($impl_lifetime),* > {
/// Raw mask from Capstone
pub(crate) fn $mask_getter(&self) -> $mask_getter_ty {
$get_mask(self)

Check failure on line 166 in capstone-rs/src/constants.rs

View workflow job for this annotation

GitHub Actions / test

try not to call a closure in the expression where it is declared

Check failure on line 166 in capstone-rs/src/constants.rs

View workflow job for this annotation

GitHub Actions / test

try not to call a closure in the expression where it is declared
}

$(
$( #[$attr] )*
pub fn $getter(&self) -> bool {
($get_mask(self) & $mask_constant) != 0

Check failure on line 172 in capstone-rs/src/constants.rs

View workflow job for this annotation

GitHub Actions / test

try not to call a closure in the expression where it is declared

Check failure on line 172 in capstone-rs/src/constants.rs

View workflow job for this annotation

GitHub Actions / test

try not to call a closure in the expression where it is declared
}
)*
}
Expand Down Expand Up @@ -221,6 +221,8 @@
=> 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 @@
=> 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 @@ -377,7 +377,7 @@

/// Iterator over instruction group ids
#[derive(Debug, Clone)]
pub struct InsnGroupIter<'a>(slice::Iter<'a, InsnGroupIdInt>);

Check failure on line 380 in capstone-rs/src/instruction.rs

View workflow job for this annotation

GitHub Actions / test

field `0` is never read

impl<'a> InsnDetail<'a> {
#[cfg(feature = "full")]
Expand Down Expand Up @@ -440,6 +440,7 @@
[TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x]
[X86, X86Detail, X86InsnDetail, x86]
[XCORE, XcoreDetail, XcoreInsnDetail, xcore]
[BPF, BpfDetail, BpfInsnDetail, bpf]
);
}
}
Expand Down
193 changes: 193 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,185 @@ 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
}
}
}

#[test]
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
}
}
}

#[test]
fn test_arch_bpf_detail() {
use crate::arch::bpf::BpfOperand::*;
use crate::arch::bpf::BpfReg::*;
use crate::arch::bpf::*;
use capstone_sys::*;

test_arch_mode_endian_insns_detail(
&mut Capstone::new()
.bpf()
.mode(bpf::ArchMode::Ebpf)
.endian(Endian::Little)
.detail(true)
.build()
.unwrap(),
Arch::BPF,
Mode::Ebpf,
None,
&[],
&[
// r1 = 0x1
DII::new(
"mov64",
b"\xb7\x01\x00\x00\x01\x00\x00\x00",
&[Reg(RegId(BPF_REG_R1 as RegIdInt)), Imm(1)],
),
// r0 = *(u32 *)(r10 - 0xc)
DII::new(
"ldxw",
b"\x61\xa0\xf4\xff\x00\x00\x00\x00",
&[
Reg(RegId(BPF_REG_R0 as RegIdInt)),
Mem(BpfOpMem(bpf_op_mem {
base: BPF_REG_R10,
disp: 0xfff4,
})),
],
),
// *(u32 *)(r10 - 0xc) = r1
DII::new(
"stxw",
b"\x63\x1a\xf4\xff\x00\x00\x00\x00",
&[
Mem(BpfOpMem(bpf_op_mem {
base: BPF_REG_R10,
disp: 0xfff4,
})),
Reg(RegId(BPF_REG_R1 as RegIdInt)),
],
),
// exit
DII::new("exit", b"\x95\x00\x00\x00\x00\x00\x00\x00", &[]),
],
);

test_arch_mode_endian_insns_detail(
&mut Capstone::new()
.bpf()
.mode(bpf::ArchMode::Cbpf)
.endian(Endian::Little)
.detail(true)
.build()
.unwrap(),
Arch::BPF,
Mode::Cbpf,
None,
&[],
&[
DII::new("txa", b"\x87\x00\x00\x00\x00\x00\x00\x00", &[]),
DII::new(
"ret",
b"\x16\x00\x00\x00\x00\x00\x00\x00",
&[Reg(RegId(BPF_REG_A as RegIdInt))],
),
],
);
}
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
1 change: 1 addition & 0 deletions 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
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
Loading
Loading