Skip to content

Commit 74c97e9

Browse files
committed
Automatically call regs_access()
When getting instruction details, we now eagerly call regs_access() to get "extended" read/write registers. This should be much simpler for users of this library.
1 parent 24ffa6b commit 74c97e9

File tree

5 files changed

+230
-71
lines changed

5 files changed

+230
-71
lines changed

Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

capstone-rs/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ travis-ci = { repository = "capstone-rust/capstone-rs" }
1818
[dependencies]
1919
capstone-sys = { path = "../capstone-sys", version = "0.17.0", default-features = false }
2020
libc = { version = "0.2", default-features = false }
21+
static_assertions = "1.1.0"
2122

2223
[dev-dependencies]
2324
criterion = "0.5"

capstone-rs/src/capstone.rs

+62-38
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use alloc::boxed::Box;
12
use alloc::string::String;
23
use core::convert::From;
34
use core::marker::PhantomData;
@@ -10,11 +11,23 @@ use capstone_sys::*;
1011

1112
use crate::arch::CapstoneBuilder;
1213
use crate::constants::{Arch, Endian, ExtraMode, Mode, OptValue, Syntax};
13-
use crate::error::*;
1414
use crate::instruction::{Insn, InsnDetail, InsnGroupId, InsnId, Instructions, RegId};
15+
use crate::{error::*, PartialInitRegsAccess};
1516

1617
use {crate::ffi::str_from_cstr_ptr, alloc::string::ToString, libc::c_uint};
1718

19+
/// Length of `cs_regs`
20+
pub(crate) const REGS_ACCESS_BUF_LEN: usize = const {
21+
// SAFETY: integers can have 0 value; in const context
22+
let regs: cs_regs = unsafe { core::mem::zeroed() };
23+
regs.len()
24+
};
25+
26+
/// Equivalent to `MaybeUninit<cs_regs>`
27+
pub(crate) type RegsAccessBuf = [MaybeUninit<RegId>; REGS_ACCESS_BUF_LEN];
28+
29+
static_assertions::assert_eq_size!(RegsAccessBuf, cs_regs);
30+
1831
/// An instance of the capstone disassembler
1932
///
2033
/// Create with an instance with [`.new()`](Self::new) and disassemble bytes with [`.disasm_all()`](Self::disasm_all).
@@ -101,17 +114,19 @@ impl Iterator for EmptyExtraModeIter {
101114
}
102115
}
103116

104-
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
105-
pub struct RegAccess {
106-
pub read: Vec<RegId>,
107-
pub write: Vec<RegId>,
117+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
118+
pub struct RegAccessRef<'a> {
119+
pub(crate) read: &'a [RegId],
120+
pub(crate) write: &'a [RegId],
108121
}
109122

110-
impl RegAccess {
111-
/// Sort read and write fields
112-
pub fn sort(&mut self) {
113-
self.read.sort_unstable();
114-
self.write.sort_unstable();
123+
impl RegAccessRef<'_> {
124+
pub fn read(&self) -> &[RegId] {
125+
self.read
126+
}
127+
128+
pub fn write(&self) -> &[RegId] {
129+
self.write
115130
}
116131
}
117132

@@ -380,39 +395,24 @@ impl Capstone {
380395
}
381396
}
382397

383-
// todo(tmfink): integrate into regs_read()/regs_write() methods to avoid the need to call
384-
/// Get the registers which are read and written
385-
pub fn regs_access(&self, insn: &Insn) -> CsResult<RegAccess> {
386-
let mut read = Vec::new();
387-
let mut write = Vec::new();
388-
389-
self.regs_access_buf(insn, &mut read, &mut write)?;
390-
391-
Ok(RegAccess { read, write })
392-
}
393-
394-
/// Get the registers are which are read and written.
395-
/// The registers are pushed to the back of the provided buffers
396-
pub(crate) fn regs_access_buf(
398+
/// Get the registers are which are read and written
399+
pub(crate) fn regs_access<'buf>(
397400
&self,
398401
insn: &Insn,
399-
read: &mut Vec<RegId>,
400-
write: &mut Vec<RegId>,
401-
) -> CsResult<()> {
402+
regs_read: &'buf mut RegsAccessBuf,
403+
regs_write: &'buf mut RegsAccessBuf,
404+
) -> CsResult<RegAccessRef<'buf>> {
402405
if cfg!(feature = "full") {
403406
let mut regs_read_count: u8 = 0;
404407
let mut regs_write_count: u8 = 0;
405408

406-
let mut regs_write: MaybeUninit<cs_regs> = MaybeUninit::uninit();
407-
let mut regs_read: MaybeUninit<cs_regs> = MaybeUninit::uninit();
408-
409409
let err = unsafe {
410410
cs_regs_access(
411411
self.csh(),
412412
&insn.insn as *const cs_insn,
413-
regs_read.as_mut_ptr(),
413+
regs_read.as_mut_ptr() as *mut cs_regs,
414414
&mut regs_read_count as *mut _,
415-
regs_write.as_mut_ptr(),
415+
regs_write.as_mut_ptr() as *mut cs_regs,
416416
&mut regs_write_count as *mut _,
417417
)
418418
};
@@ -423,23 +423,24 @@ impl Capstone {
423423

424424
// SAFETY: count indicates how many elements are initialized;
425425
let regs_read_slice: &[RegId] = unsafe {
426-
std::slice::from_raw_parts(
426+
core::slice::from_raw_parts(
427427
regs_read.as_mut_ptr() as *mut RegId,
428428
regs_read_count as usize,
429429
)
430430
};
431-
read.extend_from_slice(regs_read_slice);
432431

433432
// SAFETY: count indicates how many elements are initialized
434433
let regs_write_slice: &[RegId] = unsafe {
435-
std::slice::from_raw_parts(
434+
core::slice::from_raw_parts(
436435
regs_write.as_mut_ptr() as *mut RegId,
437436
regs_write_count as usize,
438437
)
439438
};
440-
write.extend_from_slice(regs_write_slice);
441439

442-
Ok(())
440+
Ok(RegAccessRef {
441+
read: regs_read_slice,
442+
write: regs_write_slice,
443+
})
443444
} else {
444445
Err(Error::DetailOff)
445446
}
@@ -472,7 +473,30 @@ impl Capstone {
472473
} else if insn.id().0 == 0 {
473474
Err(Error::IrrelevantDataInSkipData)
474475
} else {
475-
Ok(unsafe { insn.detail(self.arch) })
476+
// Call regs_access to get "extra" read/write registers for the instruction.
477+
// Capstone only supports this for some architectures, so ignore errors if there are
478+
// any.
479+
//
480+
// This *could* results in wasted effort if the read/write regs are not checked. As
481+
// an optimization, we could call regs_access() lazily (i.e. only if InsnDetail
482+
// regs_read()/regs_write() are called).
483+
let partial_init_regs_access = {
484+
let mut regs_buf = Box::new(crate::RWRegsAccessBuf::new());
485+
match self.regs_access(insn, &mut regs_buf.read_buf, &mut regs_buf.write_buf) {
486+
Ok(regs_access) => {
487+
let read_len = regs_access.read.len() as u16;
488+
let write_len = regs_access.write.len() as u16;
489+
Some(PartialInitRegsAccess {
490+
regs_buf,
491+
read_len,
492+
write_len,
493+
})
494+
}
495+
Err(_) => None,
496+
}
497+
};
498+
499+
Ok(unsafe { insn.detail(self.arch, partial_init_regs_access) })
476500
}
477501
}
478502

capstone-rs/src/instruction.rs

+82-14
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use alloc::{self, boxed::Box};
22
use core::convert::TryFrom;
33
use core::fmt::{self, Debug, Display, Error, Formatter};
44
use core::marker::PhantomData;
5+
use core::mem::MaybeUninit;
56
use core::ops::Deref;
67
use core::slice;
78
use core::str;
@@ -12,6 +13,7 @@ use crate::arch::ArchDetail;
1213
use crate::constants::Arch;
1314

1415
use crate::ffi::str_from_cstr_ptr;
16+
use crate::{RegsAccessBuf, REGS_ACCESS_BUF_LEN};
1517

1618
/// Represents a slice of [`Insn`] returned by [`Capstone`](crate::Capstone) `disasm*()` methods.
1719
///
@@ -167,6 +169,50 @@ pub struct Insn<'a> {
167169
pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>,
168170
}
169171

172+
pub(crate) struct RWRegsAccessBuf {
173+
pub(crate) read_buf: RegsAccessBuf,
174+
pub(crate) write_buf: RegsAccessBuf,
175+
}
176+
177+
impl RWRegsAccessBuf {
178+
pub(crate) fn new() -> Self {
179+
Self {
180+
read_buf: [MaybeUninit::uninit(); REGS_ACCESS_BUF_LEN],
181+
write_buf: [MaybeUninit::uninit(); REGS_ACCESS_BUF_LEN],
182+
}
183+
}
184+
}
185+
186+
/// Contains partially initialized buffer of registers
187+
#[cfg_attr(not(feature = "full"), allow(dead_code))]
188+
pub(crate) struct PartialInitRegsAccess {
189+
pub(crate) regs_buf: Box<RWRegsAccessBuf>,
190+
pub(crate) read_len: u16,
191+
pub(crate) write_len: u16,
192+
}
193+
194+
// make sure len fields can be stored as u16
195+
static_assertions::const_assert!(crate::REGS_ACCESS_BUF_LEN <= u16::MAX as usize);
196+
197+
#[cfg_attr(not(feature = "full"), allow(dead_code))]
198+
impl PartialInitRegsAccess {
199+
unsafe fn maybeuninit_slice_to_slice(buf: &[MaybeUninit<RegId>]) -> &[RegId] {
200+
&*(buf as *const [MaybeUninit<RegId>] as *const [RegId])
201+
}
202+
203+
pub(crate) fn read(&self) -> &[RegId] {
204+
unsafe {
205+
Self::maybeuninit_slice_to_slice(&self.regs_buf.read_buf[..self.read_len as usize])
206+
}
207+
}
208+
209+
pub(crate) fn write(&self) -> &[RegId] {
210+
unsafe {
211+
Self::maybeuninit_slice_to_slice(&self.regs_buf.write_buf[..self.write_len as usize])
212+
}
213+
}
214+
}
215+
170216
/// Contains architecture-independent details about an [`Insn`].
171217
///
172218
/// To get more detail about the instruction, enable extra details for the
@@ -196,7 +242,13 @@ pub struct Insn<'a> {
196242
/// To get additional architecture-specific information, use the
197243
/// [`.arch_detail()`](Self::arch_detail) method to get an `ArchDetail` enum.
198244
///
199-
pub struct InsnDetail<'a>(pub(crate) &'a cs_detail, pub(crate) Arch);
245+
pub struct InsnDetail<'a> {
246+
pub(crate) detail: &'a cs_detail,
247+
pub(crate) arch: Arch,
248+
249+
#[cfg_attr(not(feature = "full"), allow(dead_code))]
250+
partial_init_regs_access: Option<PartialInitRegsAccess>,
251+
}
200252

201253
#[allow(clippy::len_without_is_empty)]
202254
impl Insn<'_> {
@@ -275,8 +327,16 @@ impl Insn<'_> {
275327
/// # Safety
276328
/// The [`cs_insn::detail`] pointer must be valid and non-null.
277329
#[inline]
278-
pub(crate) unsafe fn detail(&self, arch: Arch) -> InsnDetail {
279-
InsnDetail(&*self.insn.detail, arch)
330+
pub(crate) unsafe fn detail(
331+
&self,
332+
arch: Arch,
333+
partial_init_regs_access: Option<PartialInitRegsAccess>,
334+
) -> InsnDetail<'_> {
335+
InsnDetail {
336+
detail: &*self.insn.detail,
337+
arch,
338+
partial_init_regs_access,
339+
}
280340
}
281341
}
282342

@@ -371,28 +431,36 @@ impl Display for OwnedInsn<'_> {
371431

372432
impl InsnDetail<'_> {
373433
#[cfg(feature = "full")]
374-
/// Returns the implicit read registers
434+
/// Returns the read registers
375435
pub fn regs_read(&self) -> &[RegId] {
376-
unsafe {
377-
&*(&self.0.regs_read[..self.0.regs_read_count as usize] as *const [RegIdInt]
378-
as *const [RegId])
436+
if let Some(partial) = self.partial_init_regs_access.as_ref() {
437+
partial.read()
438+
} else {
439+
unsafe {
440+
&*(&self.detail.regs_read[..self.detail.regs_read_count as usize]
441+
as *const [RegIdInt] as *const [RegId])
442+
}
379443
}
380444
}
381445

382446
#[cfg(feature = "full")]
383-
/// Returns the implicit write registers
447+
/// Returns the written to registers
384448
pub fn regs_write(&self) -> &[RegId] {
385-
unsafe {
386-
&*(&self.0.regs_write[..self.0.regs_write_count as usize] as *const [RegIdInt]
387-
as *const [RegId])
449+
if let Some(partial) = self.partial_init_regs_access.as_ref() {
450+
partial.write()
451+
} else {
452+
unsafe {
453+
&*(&self.detail.regs_write[..self.detail.regs_write_count as usize]
454+
as *const [RegIdInt] as *const [RegId])
455+
}
388456
}
389457
}
390458

391459
#[cfg(feature = "full")]
392460
/// Returns the groups to which this instruction belongs
393461
pub fn groups(&self) -> &[InsnGroupId] {
394462
unsafe {
395-
&*(&self.0.groups[..self.0.groups_count as usize] as *const [InsnGroupIdInt]
463+
&*(&self.detail.groups[..self.detail.groups_count as usize] as *const [InsnGroupIdInt]
396464
as *const [InsnGroupId])
397465
}
398466
}
@@ -407,10 +475,10 @@ impl InsnDetail<'_> {
407475
use crate::Arch::*;
408476
$( use crate::arch::$arch::$insn_detail; )*
409477

410-
return match self.1 {
478+
return match self.arch {
411479
$(
412480
$ARCH => {
413-
$detail($insn_detail(unsafe { &self.0.__bindgen_anon_1.$arch }))
481+
$detail($insn_detail(unsafe { &self.detail.__bindgen_anon_1.$arch }))
414482
}
415483
)*
416484
}

0 commit comments

Comments
 (0)