Skip to content

Commit a6b546c

Browse files
committed
Improve FileAccessor in Rust API
- Add unit tests - Expose read/write functionality - Add some much needed documentation when passing to memory map
1 parent 936cd8d commit a6b546c

File tree

4 files changed

+108
-41
lines changed

4 files changed

+108
-41
lines changed

rust/src/binary_view.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use crate::confidence::Conf;
3232
use crate::data_buffer::DataBuffer;
3333
use crate::debuginfo::DebugInfo;
3434
use crate::external_library::{ExternalLibrary, ExternalLocation};
35-
use crate::file_accessor::FileAccessor;
35+
use crate::file_accessor::{Accessor, FileAccessor};
3636
use crate::file_metadata::FileMetadata;
3737
use crate::flowgraph::FlowGraph;
3838
use crate::function::{Function, NativeBlock};
@@ -1832,8 +1832,11 @@ impl BinaryView {
18321832
unsafe { Ok(Ref::new(Self { handle })) }
18331833
}
18341834

1835-
pub fn from_accessor(meta: &FileMetadata, file: &mut FileAccessor) -> Result<Ref<Self>> {
1836-
let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.api_object) };
1835+
pub fn from_accessor<A: Accessor>(
1836+
meta: &FileMetadata,
1837+
file: &mut FileAccessor<A>,
1838+
) -> Result<Ref<Self>> {
1839+
let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.raw) };
18371840

18381841
if handle.is_null() {
18391842
return Err(());
@@ -1875,8 +1878,8 @@ impl BinaryView {
18751878
///
18761879
/// To avoid the above issue use [`crate::main_thread::execute_on_main_thread_and_wait`] to verify there
18771880
/// are no queued up main thread actions.
1878-
pub fn save_to_accessor(&self, file: &mut FileAccessor) -> bool {
1879-
unsafe { BNSaveToFile(self.handle, &mut file.api_object) }
1881+
pub fn save_to_accessor<A: Accessor>(&self, file: &mut FileAccessor<A>) -> bool {
1882+
unsafe { BNSaveToFile(self.handle, &mut file.raw) }
18801883
}
18811884
}
18821885

rust/src/binary_view/memory_map.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::binary_view::BinaryView;
22
use crate::data_buffer::DataBuffer;
3-
use crate::file_accessor::FileAccessor;
3+
use crate::file_accessor::{Accessor, FileAccessor};
44
use crate::rc::Ref;
55
use crate::segment::SegmentFlags;
66
use crate::string::{BnString, IntoCStr};
@@ -59,6 +59,9 @@ impl MemoryMap {
5959
}
6060
}
6161

62+
/// Adds the memory region using a [`DataBuffer`].
63+
///
64+
/// This will add the contents of the [`DataBuffer`] to the database.
6265
pub fn add_data_memory_region(
6366
&mut self,
6467
name: &str,
@@ -78,11 +81,20 @@ impl MemoryMap {
7881
}
7982
}
8083

81-
pub fn add_remote_memory_region(
84+
// TODO: This really cant be safe until BNFileAccessor is ARC'd and can be freed. Probably need another thing
85+
// TODO: Ontop of a file accessor in the core that would manage it. (I.e. BNFileAccessorHandle) or something.
86+
/// Adds the memory region using a [`FileAccessor`].
87+
///
88+
/// This does not add the region contents to the database, instead accesses to the contents
89+
/// are done "remotely" to a [`FileAccessor`].
90+
///
91+
/// NOTE: The [`FileAccessor`] MUST live as long as the region is available, currently there is no gurentee by
92+
/// the type checker that the file accessor is tied to that of the memory region.
93+
pub fn add_remote_memory_region<A: Accessor>(
8294
&mut self,
8395
name: &str,
8496
start: u64,
85-
accessor: &mut FileAccessor,
97+
accessor: &mut FileAccessor<A>,
8698
segment_flags: Option<SegmentFlags>,
8799
) -> bool {
88100
let name_raw = name.to_cstr();
@@ -91,7 +103,7 @@ impl MemoryMap {
91103
self.view.handle,
92104
name_raw.as_ptr(),
93105
start,
94-
&mut accessor.api_object,
106+
&mut accessor.raw,
95107
segment_flags.unwrap_or_default().into_raw(),
96108
)
97109
}

rust/src/file_accessor.rs

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,36 @@
1313
// limitations under the License.
1414

1515
use binaryninjacore_sys::BNFileAccessor;
16-
use std::io::{Read, Seek, SeekFrom, Write};
16+
use std::io::{ErrorKind, Read, Seek, SeekFrom, Write};
1717
use std::marker::PhantomData;
1818
use std::slice;
1919

20-
pub struct FileAccessor<'a> {
21-
pub(crate) api_object: BNFileAccessor,
22-
_ref: PhantomData<&'a mut ()>,
20+
pub trait Accessor: Read + Write + Seek + Sized {}
21+
22+
impl<T: Read + Write + Seek + Sized> Accessor for T {}
23+
24+
pub struct FileAccessor<A: Accessor> {
25+
pub(crate) raw: BNFileAccessor,
26+
accessor: PhantomData<A>,
2327
}
2428

25-
impl<'a> FileAccessor<'a> {
26-
pub fn new<F>(f: &'a mut F) -> Self
27-
where
28-
F: 'a + Read + Write + Seek + Sized,
29-
{
29+
impl<A: Accessor> FileAccessor<A> {
30+
pub fn new(accessor: A) -> Self {
3031
use std::os::raw::c_void;
3132

32-
extern "C" fn cb_get_length<F>(ctxt: *mut c_void) -> u64
33-
where
34-
F: Read + Write + Seek + Sized,
35-
{
36-
let f = unsafe { &mut *(ctxt as *mut F) };
33+
extern "C" fn cb_get_length<A: Accessor>(ctxt: *mut c_void) -> u64 {
34+
let f = unsafe { &mut *(ctxt as *mut A) };
3735

3836
f.seek(SeekFrom::End(0)).unwrap_or(0)
3937
}
4038

41-
extern "C" fn cb_read<F>(
39+
extern "C" fn cb_read<A: Accessor>(
4240
ctxt: *mut c_void,
4341
dest: *mut c_void,
4442
offset: u64,
4543
len: usize,
46-
) -> usize
47-
where
48-
F: Read + Write + Seek + Sized,
49-
{
50-
let f = unsafe { &mut *(ctxt as *mut F) };
44+
) -> usize {
45+
let f = unsafe { &mut *(ctxt as *mut A) };
5146
let dest = unsafe { slice::from_raw_parts_mut(dest as *mut u8, len) };
5247

5348
if f.seek(SeekFrom::Start(offset)).is_err() {
@@ -58,16 +53,13 @@ impl<'a> FileAccessor<'a> {
5853
}
5954
}
6055

61-
extern "C" fn cb_write<F>(
56+
extern "C" fn cb_write<A: Accessor>(
6257
ctxt: *mut c_void,
6358
offset: u64,
6459
src: *const c_void,
6560
len: usize,
66-
) -> usize
67-
where
68-
F: Read + Write + Seek + Sized,
69-
{
70-
let f = unsafe { &mut *(ctxt as *mut F) };
61+
) -> usize {
62+
let f = unsafe { &mut *(ctxt as *mut A) };
7163
let src = unsafe { slice::from_raw_parts(src as *const u8, len) };
7264

7365
if f.seek(SeekFrom::Start(offset)).is_err() {
@@ -77,14 +69,52 @@ impl<'a> FileAccessor<'a> {
7769
}
7870
}
7971

72+
let boxed_accessor = Box::new(accessor);
73+
let leaked_accessor = Box::leak(boxed_accessor);
74+
8075
Self {
81-
api_object: BNFileAccessor {
82-
context: f as *mut F as *mut _,
83-
getLength: Some(cb_get_length::<F>),
84-
read: Some(cb_read::<F>),
85-
write: Some(cb_write::<F>),
76+
raw: BNFileAccessor {
77+
context: leaked_accessor as *mut A as *mut _,
78+
getLength: Some(cb_get_length::<A>),
79+
read: Some(cb_read::<A>),
80+
write: Some(cb_write::<A>),
8681
},
87-
_ref: PhantomData,
82+
accessor: PhantomData,
83+
}
84+
}
85+
86+
pub fn read(&self, addr: u64, len: usize) -> Result<Vec<u8>, ErrorKind> {
87+
let cb_read = self.raw.read.unwrap();
88+
let mut buf = vec![0; len];
89+
let read_len = unsafe { cb_read(self.raw.context, buf.as_mut_ptr() as *mut _, addr, len) };
90+
if read_len != len {
91+
return Err(ErrorKind::UnexpectedEof);
92+
}
93+
Ok(buf)
94+
}
95+
96+
pub fn write(&self, addr: u64, data: &[u8]) -> usize {
97+
let cb_write = self.raw.write.unwrap();
98+
unsafe {
99+
cb_write(
100+
self.raw.context,
101+
addr,
102+
data.as_ptr() as *const _,
103+
data.len(),
104+
)
105+
}
106+
}
107+
108+
pub fn length(&self) -> u64 {
109+
let cb_get_length = self.raw.getLength.unwrap();
110+
unsafe { cb_get_length(self.raw.context) }
111+
}
112+
}
113+
114+
impl<A: Accessor> Drop for FileAccessor<A> {
115+
fn drop(&mut self) {
116+
unsafe {
117+
let _ = Box::from_raw(self.raw.context as *mut A);
88118
}
89119
}
90120
}

rust/tests/file_accessor.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use binaryninja::file_accessor::FileAccessor;
2+
use std::io::Cursor;
3+
4+
#[test]
5+
fn test_file_accessor() {
6+
let mut mock_data = Cursor::new(vec![0u8; 100]);
7+
let accessor = FileAccessor::new(&mut mock_data);
8+
assert_eq!(
9+
accessor.length(),
10+
100,
11+
"File accessor length does not match"
12+
);
13+
assert_eq!(
14+
accessor.write(0x10, &[0xff]),
15+
1,
16+
"Failed to write to file accessor"
17+
);
18+
let read_value = accessor
19+
.read(0x10, 1)
20+
.expect("Failed to read from file accessor");
21+
assert_eq!(read_value, &[0xff], "Read value does not match");
22+
}

0 commit comments

Comments
 (0)