Skip to content

Commit 0fbf3c1

Browse files
authored
Add Support for BPF arch (#144)
1 parent 752d24d commit 0fbf3c1

File tree

10 files changed

+593
-83
lines changed

10 files changed

+593
-83
lines changed

capstone-rs/src/arch/bpf.rs

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//! Contains bpf specific types
2+
3+
use core::convert::From;
4+
use core::{cmp, fmt, slice};
5+
6+
pub use capstone_sys::bpf_insn_group as BpfInsnGroup;
7+
pub use capstone_sys::bpf_insn as BpfInsn;
8+
pub use capstone_sys::bpf_reg as BpfReg;
9+
use capstone_sys::{cs_bpf, cs_bpf_op, bpf_op_mem, bpf_op_type};
10+
11+
pub use crate::arch::arch_builder::bpf::*;
12+
use crate::arch::DetailsArchInsn;
13+
use crate::instruction::{RegId, RegIdInt};
14+
15+
/// Contains BPF-specific details for an instruction
16+
pub struct BpfInsnDetail<'a>(pub(crate) &'a cs_bpf);
17+
18+
impl_PartialEq_repr_fields!(BpfInsnDetail<'a> [ 'a ];
19+
operands
20+
);
21+
22+
/// BPF operand
23+
#[derive(Clone, Debug, Eq, PartialEq)]
24+
pub enum BpfOperand {
25+
/// Register
26+
Reg(RegId),
27+
28+
/// Immediate
29+
Imm(u64),
30+
31+
/// Memory
32+
Mem(BpfOpMem),
33+
34+
/// Offset
35+
Off(u32),
36+
37+
/// Mmem
38+
Mmem(u32),
39+
40+
/// Msh
41+
Msh(u32),
42+
43+
/// Ext
44+
Ext(u32),
45+
46+
/// Invalid
47+
Invalid,
48+
}
49+
50+
impl Default for BpfOperand {
51+
fn default() -> Self {
52+
BpfOperand::Invalid
53+
}
54+
}
55+
56+
57+
/// Bpf memory operand
58+
#[derive(Debug, Copy, Clone)]
59+
pub struct BpfOpMem(pub(crate) bpf_op_mem);
60+
61+
impl BpfOpMem {
62+
/// Base register
63+
pub fn base(&self) -> RegId {
64+
RegId(self.0.base as RegIdInt)
65+
}
66+
67+
/// Disp value
68+
pub fn disp(&self) -> u32 {
69+
self.0.disp
70+
}
71+
}
72+
73+
impl_PartialEq_repr_fields!(BpfOpMem;
74+
base, disp
75+
);
76+
77+
impl cmp::Eq for BpfOpMem {}
78+
79+
impl<'a> From<&'a cs_bpf_op> for BpfOperand {
80+
fn from(insn: &cs_bpf_op) -> BpfOperand {
81+
match insn.type_ {
82+
bpf_op_type::BPF_OP_EXT => BpfOperand::Ext(unsafe { insn.__bindgen_anon_1.ext }),
83+
bpf_op_type::BPF_OP_INVALID => BpfOperand::Invalid,
84+
bpf_op_type::BPF_OP_REG => BpfOperand::Reg(RegId(unsafe {insn.__bindgen_anon_1.reg} as RegIdInt)),
85+
bpf_op_type::BPF_OP_IMM => BpfOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }),
86+
bpf_op_type::BPF_OP_MEM => BpfOperand::Mem(BpfOpMem(unsafe { insn.__bindgen_anon_1.mem})),
87+
bpf_op_type::BPF_OP_OFF => BpfOperand::Off(unsafe { insn.__bindgen_anon_1.off }),
88+
bpf_op_type::BPF_OP_MMEM => BpfOperand::Mmem(unsafe { insn.__bindgen_anon_1.mmem }),
89+
bpf_op_type::BPF_OP_MSH => BpfOperand::Msh(unsafe { insn.__bindgen_anon_1.msh }),
90+
}
91+
}
92+
}
93+
94+
def_arch_details_struct!(
95+
InsnDetail = BpfInsnDetail;
96+
Operand = BpfOperand;
97+
OperandIterator = BpfOperandIterator;
98+
OperandIteratorLife = BpfOperandIterator<'a>;
99+
[ pub struct BpfOperandIterator<'a>(slice::Iter<'a, cs_bpf_op>); ]
100+
cs_arch_op = cs_bpf_op;
101+
cs_arch = cs_bpf;
102+
);

capstone-rs/src/arch/mod.rs

+17
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,16 @@ macro_rules! arch_info_base {
352352
( syntax: )
353353
( both_endian: false )
354354
]
355+
[
356+
( bpf, BPF )
357+
( mode:
358+
Cbpf,
359+
Ebpf,
360+
)
361+
( extra_modes: )
362+
( syntax: )
363+
( both_endian: true )
364+
]
355365
);
356366
};
357367
}
@@ -534,6 +544,13 @@ macro_rules! detail_arch_base {
534544
/// Returns the XCore details, if any
535545
=> arch_name = xcore,
536546
]
547+
[
548+
detail = BpfDetail,
549+
insn_detail = BpfInsnDetail<'a>,
550+
op = BpfOperand,
551+
/// Returns the XCore details, if any
552+
=> arch_name = bpf,
553+
]
537554
);
538555
};
539556
}

capstone-rs/src/constants.rs

+6
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ define_cs_enum_wrapper!(
221221
=> EVM = CS_ARCH_EVM;
222222
/// RISC-V
223223
=> RISCV = CS_ARCH_RISCV;
224+
/// BPF
225+
=> BPF = CS_ARCH_BPF;
224226
);
225227

226228
define_cs_enum_wrapper!(
@@ -286,6 +288,10 @@ define_cs_enum_wrapper!(
286288
=> RiscV32 = CS_MODE_RISCV32;
287289
/// RISC-V 64-bit mode
288290
=> RiscV64 = CS_MODE_RISCV64;
291+
/// Classic BPF mode
292+
=> Cbpf = CS_MODE_BPF_CLASSIC;
293+
/// Extended BPF mode
294+
=> Ebpf = CS_MODE_BPF_EXTENDED;
289295
/// Default mode for little-endian
290296
=> Default = CS_MODE_LITTLE_ENDIAN;
291297
);

capstone-rs/src/instruction.rs

+1
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ impl<'a> InsnDetail<'a> {
440440
[TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x]
441441
[X86, X86Detail, X86InsnDetail, x86]
442442
[XCORE, XcoreDetail, XcoreInsnDetail, xcore]
443+
[BPF, BpfDetail, BpfInsnDetail, bpf]
443444
);
444445
}
445446
}

capstone-rs/src/test.rs

+193
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ use super::*;
1717

1818
const X86_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00";
1919
const ARM_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00";
20+
const CBPF_CODE: &[u8] = b"\x94\x09\x00\x00\x37\x13\x03\x00\
21+
\x87\x00\x00\x00\x00\x00\x00\x00\
22+
\x07\x00\x00\x00\x00\x00\x00\x00\
23+
\x16\x00\x00\x00\x00\x00\x00\x00\
24+
\x80\x00\x00\x00\x00\x00\x00\x00";
25+
const EBPF_CODE: &[u8] = b"\x97\x09\x00\x00\x37\x13\x03\x00\
26+
\xdc\x02\x00\x00\x20\x00\x00\x00\
27+
\x30\x00\x00\x00\x00\x00\x00\x00\
28+
\xdb\x3a\x00\x01\x00\x00\x00\x00\
29+
\x84\x02\x00\x00\x00\x00\x00\x00\
30+
\x6d\x33\x17\x02\x00\x00\x00\x00";
2031

2132
// Aliases for group types
2233
const JUMP: cs_group_type::Type = cs_group_type::CS_GRP_JUMP;
@@ -3244,3 +3255,185 @@ fn test_owned_insn() {
32443255
assert_eq!(format!("{:?}", insn), format!("{:?}", owned));
32453256
}
32463257
}
3258+
3259+
/// Print register names
3260+
fn reg_names(cs: &Capstone, regs: &[RegId]) -> String {
3261+
let names: Vec<String> = regs.iter().map(|&x| cs.reg_name(x).unwrap()).collect();
3262+
names.join(", ")
3263+
}
3264+
3265+
/// Print instruction group names
3266+
fn group_names(cs: &Capstone, regs: &[InsnGroupId]) -> String {
3267+
let names: Vec<String> = regs.iter().map(|&x| cs.group_name(x).unwrap()).collect();
3268+
names.join(", ")
3269+
}
3270+
3271+
#[test]
3272+
fn test_cbpf() {
3273+
let cs = Capstone::new()
3274+
.bpf()
3275+
.mode(bpf::ArchMode::Cbpf)
3276+
.endian(Endian::Little)
3277+
.detail(true)
3278+
.build()
3279+
.unwrap();
3280+
let insns = cs.disasm_all(CBPF_CODE, 0x1000);
3281+
match insns {
3282+
Ok(ins) => {
3283+
for i in ins.as_ref() {
3284+
println!();
3285+
eprintln!("{}", i);
3286+
3287+
let detail: InsnDetail = cs.insn_detail(&i).expect("Failed to get insn detail");
3288+
let arch_detail: ArchDetail = detail.arch_detail();
3289+
let ops = arch_detail.operands();
3290+
3291+
let output: &[(&str, String)] = &[
3292+
("insn id:", format!("{:?}", i.id().0)),
3293+
("bytes:", format!("{:?}", i.bytes())),
3294+
("read regs:", reg_names(&cs, detail.regs_read())),
3295+
("write regs:", reg_names(&cs, detail.regs_write())),
3296+
("insn groups:", group_names(&cs, detail.groups())),
3297+
];
3298+
3299+
for &(ref name, ref message) in output.iter() {
3300+
eprintln!("{:4}{:12} {}", "", name, message);
3301+
}
3302+
3303+
println!("{:4}operands: {}", "", ops.len());
3304+
for op in ops {
3305+
eprintln!("{:8}{:?}", "", op);
3306+
}
3307+
}
3308+
}
3309+
3310+
Err(e) => {
3311+
eprintln!("{:?}", e);
3312+
assert!(false);
3313+
}
3314+
}
3315+
}
3316+
3317+
#[test]
3318+
fn test_ebpf() {
3319+
let cs = Capstone::new()
3320+
.bpf()
3321+
.mode(bpf::ArchMode::Ebpf)
3322+
.endian(Endian::Little)
3323+
.detail(true)
3324+
.build()
3325+
.unwrap();
3326+
let insns = cs.disasm_all(EBPF_CODE, 0x1000);
3327+
match insns {
3328+
Ok(ins) => {
3329+
for i in ins.as_ref() {
3330+
println!();
3331+
eprintln!("{}", i);
3332+
3333+
let detail: InsnDetail = cs.insn_detail(&i).expect("Failed to get insn detail");
3334+
let arch_detail: ArchDetail = detail.arch_detail();
3335+
let ops = arch_detail.operands();
3336+
3337+
let output: &[(&str, String)] = &[
3338+
("insn id:", format!("{:?}", i.id().0)),
3339+
("bytes:", format!("{:?}", i.bytes())),
3340+
("read regs:", reg_names(&cs, detail.regs_read())),
3341+
("write regs:", reg_names(&cs, detail.regs_write())),
3342+
("insn groups:", group_names(&cs, detail.groups())),
3343+
];
3344+
3345+
for &(ref name, ref message) in output.iter() {
3346+
eprintln!("{:4}{:12} {}", "", name, message);
3347+
}
3348+
3349+
println!("{:4}operands: {}", "", ops.len());
3350+
for op in ops {
3351+
eprintln!("{:8}{:?}", "", op);
3352+
}
3353+
}
3354+
}
3355+
3356+
Err(e) => {
3357+
eprintln!("{:?}", e);
3358+
assert!(false);
3359+
}
3360+
}
3361+
}
3362+
3363+
#[test]
3364+
fn test_arch_bpf_detail() {
3365+
use crate::arch::bpf::BpfOperand::*;
3366+
use crate::arch::bpf::BpfReg::*;
3367+
use crate::arch::bpf::*;
3368+
use capstone_sys::*;
3369+
3370+
test_arch_mode_endian_insns_detail(
3371+
&mut Capstone::new()
3372+
.bpf()
3373+
.mode(bpf::ArchMode::Ebpf)
3374+
.endian(Endian::Little)
3375+
.detail(true)
3376+
.build()
3377+
.unwrap(),
3378+
Arch::BPF,
3379+
Mode::Ebpf,
3380+
None,
3381+
&[],
3382+
&[
3383+
// r1 = 0x1
3384+
DII::new(
3385+
"mov64",
3386+
b"\xb7\x01\x00\x00\x01\x00\x00\x00",
3387+
&[Reg(RegId(BPF_REG_R1 as RegIdInt)), Imm(1)],
3388+
),
3389+
// r0 = *(u32 *)(r10 - 0xc)
3390+
DII::new(
3391+
"ldxw",
3392+
b"\x61\xa0\xf4\xff\x00\x00\x00\x00",
3393+
&[
3394+
Reg(RegId(BPF_REG_R0 as RegIdInt)),
3395+
Mem(BpfOpMem(bpf_op_mem {
3396+
base: BPF_REG_R10,
3397+
disp: 0xfff4,
3398+
})),
3399+
],
3400+
),
3401+
// *(u32 *)(r10 - 0xc) = r1
3402+
DII::new(
3403+
"stxw",
3404+
b"\x63\x1a\xf4\xff\x00\x00\x00\x00",
3405+
&[
3406+
Mem(BpfOpMem(bpf_op_mem {
3407+
base: BPF_REG_R10,
3408+
disp: 0xfff4,
3409+
})),
3410+
Reg(RegId(BPF_REG_R1 as RegIdInt)),
3411+
],
3412+
),
3413+
// exit
3414+
DII::new("exit", b"\x95\x00\x00\x00\x00\x00\x00\x00", &[]),
3415+
],
3416+
);
3417+
3418+
test_arch_mode_endian_insns_detail(
3419+
&mut Capstone::new()
3420+
.bpf()
3421+
.mode(bpf::ArchMode::Cbpf)
3422+
.endian(Endian::Little)
3423+
.detail(true)
3424+
.build()
3425+
.unwrap(),
3426+
Arch::BPF,
3427+
Mode::Cbpf,
3428+
None,
3429+
&[],
3430+
&[
3431+
DII::new("txa", b"\x87\x00\x00\x00\x00\x00\x00\x00", &[]),
3432+
DII::new(
3433+
"ret",
3434+
b"\x16\x00\x00\x00\x00\x00\x00\x00",
3435+
&[Reg(RegId(BPF_REG_A as RegIdInt))],
3436+
),
3437+
],
3438+
);
3439+
}

capstone-sys/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ travis-ci = { repository = "capstone-rust/capstone-sys" }
2626
libc = { version = "0.2.59", default-features = false }
2727

2828
[build-dependencies]
29-
bindgen = { optional = true, version = "0.59.1" }
29+
bindgen = { optional = true, version = "0.62.0" }
3030
regex = { optional = true, version = "1.3.1" }
3131
cc = "1.0"
3232

capstone-sys/build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ fn build_capstone_cc() {
133133
.define("CAPSTONE_HAS_WASM", None)
134134
.define("CAPSTONE_HAS_X86", None)
135135
.define("CAPSTONE_HAS_XCORE", None)
136+
.define("CAPSTONE_HAS_BPF", None)
136137
// No need to display any warnings from the C library
137138
.flag_if_supported("-w")
138139
.static_crt(use_static_crt);

capstone-sys/common.rs

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ pub static ARCH_INCLUDES: &[CapstoneArchInfo<'static>] = &[
7777
header_name: "xcore.h",
7878
cs_name: "xcore",
7979
},
80+
CapstoneArchInfo {
81+
header_name: "bpf.h",
82+
cs_name: "bpf"
83+
}
8084
];
8185

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

0 commit comments

Comments
 (0)