Skip to content

Commit f49aa4a

Browse files
committed
rs: expose CS_OPT_MNEMONIC option via Capstone::set_mnemonic
Fixes #64. Since the library expects a NUL-terminated string, an extra step is added to append NUL character using std::ffi::CString. However, it requires std environment, so set_mnemonic_cstr is provided for non-std environment.
1 parent 1c0c550 commit f49aa4a

File tree

4 files changed

+106
-1
lines changed

4 files changed

+106
-1
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ jobs:
4141
run: env ${{ matrix.rust.env }} ALL_FEATURES=1 ./capstone-rs/ci/test.sh
4242

4343
- name: test (only enable x86 and arm64)
44-
run: env ${{ matrix.rust.env }} FEATURES=full,arch_x86,arch_arm64 NO_DEFAULT_FEATURES=1 ./capstone-rs/ci/test.sh
44+
run: env ${{ matrix.rust.env }} FEATURES=std,full,arch_x86,arch_arm64 NO_DEFAULT_FEATURES=1 ./capstone-rs/ci/test.sh

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Make RegAccessType available for ARM64
1616
- Rename RegAccessType to AccessType while keeping type alias
1717
- Expose `CS_OPT_UNSIGNED` via `Capstone::set_unsigned`
18+
- Expose `CS_OPT_MNEMONIC` via `Capstone::set_mnemonic`
1819

1920
### Changed
2021
- `InsnDetail::regs_read()`/`InsnDetail::regs_write()` return more of the accessed registers

capstone-rs/src/capstone.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use alloc::boxed::Box;
22
use alloc::string::String;
33
use core::convert::From;
4+
use core::ffi::CStr;
45
use core::marker::PhantomData;
56
use core::mem::MaybeUninit;
7+
#[cfg(feature = "std")]
8+
use std::ffi::CString;
69

710
use libc::{c_int, c_void};
811

@@ -386,6 +389,44 @@ impl Capstone {
386389
result
387390
}
388391

392+
/// Customize mnemonic for instructions with alternative name.
393+
///
394+
/// Pass `Some(mnemonic)` to enable custom mnemonic or `None` to revert to default.
395+
#[cfg(feature = "std")]
396+
pub fn set_mnemonic(&mut self, insn_id: InsnId, mnemonic: Option<&str>) -> CsResult<()> {
397+
let mnemonic_cstr = match mnemonic {
398+
Some(s) => Some(CString::new(s).map_err(|_err| {
399+
Error::CustomError(
400+
"Failed to convert mnemonic to C-String due to internal NUL characters",
401+
)
402+
})?),
403+
None => None,
404+
};
405+
self.set_mnemonic_cstr(insn_id, mnemonic_cstr.as_ref().map(CString::as_c_str))
406+
}
407+
408+
/// Customize mnemonic for instructions with alternative name, passing a core::ffi::CStr.
409+
///
410+
/// Pass `Some(mnemonic)` to enable custom mnemonic or `None` to revert to default.
411+
pub fn set_mnemonic_cstr(
412+
&mut self,
413+
insn_id: InsnId,
414+
mnemonic_cstr: Option<&CStr>,
415+
) -> CsResult<()> {
416+
let option_value: cs_opt_mnem = cs_opt_mnem {
417+
id: insn_id.0,
418+
mnemonic: mnemonic_cstr
419+
.map(|s| s.as_ptr())
420+
.unwrap_or(core::ptr::null()),
421+
};
422+
let result = self._set_cs_option(
423+
cs_opt_type::CS_OPT_MNEMONIC,
424+
&option_value as *const cs_opt_mnem as usize,
425+
);
426+
427+
result
428+
}
429+
389430
/// Converts a register id `reg_id` to a `String` containing the register name.
390431
/// Unavailable in Diet mode
391432
pub fn reg_name(&self, reg_id: RegId) -> Option<String> {

capstone-rs/src/test.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,69 @@ fn test_unsigned() {
208208
assert_eq!(insns[0].op_str(), Some("x0, [x1, #0xffffffffffffffff]"));
209209
}
210210

211+
#[cfg(feature = "arch_x86")]
212+
#[test]
213+
fn test_mnemonic() {
214+
use capstone_sys::x86_insn;
215+
216+
let x86_code: &[u8] = b"\x6c";
217+
218+
// default mnemonic
219+
let cs = Capstone::new()
220+
.x86()
221+
.mode(x86::ArchMode::Mode64)
222+
.build()
223+
.unwrap();
224+
let insns = cs.disasm_all(x86_code, 0x1000).unwrap();
225+
let insns: Vec<_> = insns.iter().collect();
226+
assert_eq!(insns.len(), 1);
227+
assert_eq!(insns[0].id().0, x86_insn::X86_INS_INSB as u32);
228+
assert_eq!(insns[0].mnemonic(), Some("insb"));
229+
230+
// override mnemonic
231+
let mut cs = Capstone::new()
232+
.x86()
233+
.mode(x86::ArchMode::Mode64)
234+
.build()
235+
.unwrap();
236+
cs.set_mnemonic(InsnId(x86_insn::X86_INS_INSB as InsnIdInt), Some("abcd"))
237+
.unwrap();
238+
let insns = cs.disasm_all(x86_code, 0x1000).unwrap();
239+
let insns: Vec<_> = insns.iter().collect();
240+
assert_eq!(insns.len(), 1);
241+
assert_eq!(insns[0].id().0, x86_insn::X86_INS_INSB as u32);
242+
assert_eq!(insns[0].mnemonic(), Some("abcd"));
243+
244+
// revert override
245+
let mut cs = Capstone::new()
246+
.x86()
247+
.mode(x86::ArchMode::Mode64)
248+
.build()
249+
.unwrap();
250+
cs.set_mnemonic(InsnId(x86_insn::X86_INS_INSB as InsnIdInt), Some("abcd"))
251+
.unwrap();
252+
cs.set_mnemonic(InsnId(x86_insn::X86_INS_INSB as InsnIdInt), None)
253+
.unwrap();
254+
let insns = cs.disasm_all(x86_code, 0x1000).unwrap();
255+
let insns: Vec<_> = insns.iter().collect();
256+
assert_eq!(insns.len(), 1);
257+
assert_eq!(insns[0].id().0, x86_insn::X86_INS_INSB as u32);
258+
assert_eq!(insns[0].mnemonic(), Some("insb"));
259+
260+
// override with invalid mnemonic should fail
261+
let mut cs = Capstone::new()
262+
.x86()
263+
.mode(x86::ArchMode::Mode64)
264+
.build()
265+
.unwrap();
266+
assert!(cs
267+
.set_mnemonic(
268+
InsnId(x86_insn::X86_INS_INSB as InsnIdInt),
269+
Some("\x00abcd")
270+
)
271+
.is_err());
272+
}
273+
211274
#[cfg(all(feature = "full", feature = "arch_x86"))]
212275
#[test]
213276
fn test_detail_true() {

0 commit comments

Comments
 (0)