Skip to content

Commit 8ccc5f2

Browse files
committed
cs: eagerly call regs_access() in insn_detail()
When getting instruction details, we now eagerly call regs_access() to get "extended" read/write registers. This is simpler than requiring users to manually call it to get the "actual" accessed registers.
1 parent 37b3663 commit 8ccc5f2

File tree

5 files changed

+231
-71
lines changed

5 files changed

+231
-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

+63-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,24 @@ 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 = 64;
21+
22+
// todo(tmfink) When MSRV is 1.75 or later, can use:
23+
//pub(crate) const REGS_ACCESS_BUF_LEN: usize = unsafe { core::mem::zeroed::<cs_regs>() }.len();
24+
25+
/// Equivalent to `MaybeUninit<cs_regs>`
26+
pub(crate) type RegsAccessBuf = [MaybeUninit<RegId>; REGS_ACCESS_BUF_LEN];
27+
28+
static_assertions::assert_eq_size!(RegId, u16);
29+
static_assertions::assert_eq_size!(RegsAccessBuf, cs_regs);
30+
static_assertions::assert_type_eq_all!([u16; REGS_ACCESS_BUF_LEN], cs_regs);
31+
1832
/// An instance of the capstone disassembler
1933
///
2034
/// Create with an instance with [`.new()`](Self::new) and disassemble bytes with [`.disasm_all()`](Self::disasm_all).
@@ -101,17 +115,19 @@ impl Iterator for EmptyExtraModeIter {
101115
}
102116
}
103117

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

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();
124+
impl RegAccessRef<'_> {
125+
pub fn read(&self) -> &[RegId] {
126+
self.read
127+
}
128+
129+
pub fn write(&self) -> &[RegId] {
130+
self.write
115131
}
116132
}
117133

@@ -380,39 +396,24 @@ impl Capstone {
380396
}
381397
}
382398

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(
399+
/// Get the registers are which are read and written
400+
pub(crate) fn regs_access<'buf>(
397401
&self,
398402
insn: &Insn,
399-
read: &mut Vec<RegId>,
400-
write: &mut Vec<RegId>,
401-
) -> CsResult<()> {
403+
regs_read: &'buf mut RegsAccessBuf,
404+
regs_write: &'buf mut RegsAccessBuf,
405+
) -> CsResult<RegAccessRef<'buf>> {
402406
if cfg!(feature = "full") {
403407
let mut regs_read_count: u8 = 0;
404408
let mut regs_write_count: u8 = 0;
405409

406-
let mut regs_write: MaybeUninit<cs_regs> = MaybeUninit::uninit();
407-
let mut regs_read: MaybeUninit<cs_regs> = MaybeUninit::uninit();
408-
409410
let err = unsafe {
410411
cs_regs_access(
411412
self.csh(),
412413
&insn.insn as *const cs_insn,
413-
regs_read.as_mut_ptr(),
414+
regs_read.as_mut_ptr() as *mut cs_regs,
414415
&mut regs_read_count as *mut _,
415-
regs_write.as_mut_ptr(),
416+
regs_write.as_mut_ptr() as *mut cs_regs,
416417
&mut regs_write_count as *mut _,
417418
)
418419
};
@@ -423,23 +424,24 @@ impl Capstone {
423424

424425
// SAFETY: count indicates how many elements are initialized;
425426
let regs_read_slice: &[RegId] = unsafe {
426-
std::slice::from_raw_parts(
427+
core::slice::from_raw_parts(
427428
regs_read.as_mut_ptr() as *mut RegId,
428429
regs_read_count as usize,
429430
)
430431
};
431-
read.extend_from_slice(regs_read_slice);
432432

433433
// SAFETY: count indicates how many elements are initialized
434434
let regs_write_slice: &[RegId] = unsafe {
435-
std::slice::from_raw_parts(
435+
core::slice::from_raw_parts(
436436
regs_write.as_mut_ptr() as *mut RegId,
437437
regs_write_count as usize,
438438
)
439439
};
440-
write.extend_from_slice(regs_write_slice);
441440

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

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)