@@ -34,6 +34,62 @@ mod test {
3434 // not much to test for in the unit tests
3535 // the integration tests should have way more stuffs
3636
37+ #[ test]
38+ fn test_box_func_and_call ( ) {
39+ fn func ( buf : & mut [ u8 ] , size : u64 , asid : Asid ) -> i32 {
40+ assert_eq ! ( buf, & [ 10u8 , 20u8 , 30u8 , 40u8 ] ) ;
41+ assert_eq ! ( size, 50 ) ;
42+ assert ! ( asid. cr3( ) . is_none( ) ) ;
43+ assert ! ( asid. vmcs( ) . is_none( ) ) ;
44+ 42
45+ }
46+
47+ let boxed = BoxedCallback :: box_callback ( func) ;
48+
49+ let mut buf = vec ! [ 10u8 , 20u8 , 30u8 , 40u8 ] ;
50+ let ret = unsafe {
51+ BoxedCallback :: call ( boxed. 0 , & mut buf, 50 , Asid :: new ( None , None ) )
52+ } ;
53+ assert_eq ! ( ret, 42 ) ;
54+ }
55+
56+ #[ test]
57+ fn test_box_no_capture_closure_and_call ( ) {
58+ let boxed = BoxedCallback :: box_callback ( |buf, size, asid| {
59+ assert_eq ! ( buf, & [ 10u8 , 20u8 , 30u8 , 40u8 ] ) ;
60+ assert_eq ! ( size, 50 ) ;
61+ assert ! ( asid. cr3( ) . is_none( ) ) ;
62+ assert ! ( asid. vmcs( ) . is_none( ) ) ;
63+ 42
64+ } ) ;
65+
66+ let mut buf = vec ! [ 10u8 , 20u8 , 30u8 , 40u8 ] ;
67+ let ret = unsafe {
68+ BoxedCallback :: call ( boxed. 0 , & mut buf, 50 , Asid :: new ( None , None ) )
69+ } ;
70+ assert_eq ! ( ret, 42 ) ;
71+ }
72+
73+ #[ test]
74+ fn test_box_capture_closure_and_call ( ) {
75+ let data = 60 ;
76+
77+ let boxed = BoxedCallback :: box_callback ( move |buf, size, asid| {
78+ assert_eq ! ( buf, & [ 10u8 , 20u8 , 30u8 , 40u8 ] ) ;
79+ assert_eq ! ( size, 50 ) ;
80+ assert_eq ! ( data, 60 ) ;
81+ assert ! ( asid. cr3( ) . is_none( ) ) ;
82+ assert ! ( asid. vmcs( ) . is_none( ) ) ;
83+ 42
84+ } ) ;
85+
86+ let mut buf = vec ! [ 10u8 , 20u8 , 30u8 , 40u8 ] ;
87+ let ret = unsafe {
88+ BoxedCallback :: call ( boxed. 0 , & mut buf, 50 , Asid :: new ( None , None ) )
89+ } ;
90+ assert_eq ! ( ret, 42 ) ;
91+ }
92+
3793 #[ test]
3894 fn test_img_alloc ( ) {
3995 Image :: new ( None ) . unwrap ( ) ;
@@ -118,9 +174,80 @@ unsafe extern "C" fn read_callback(buffer: *mut u8,
118174 asid : * const pt_asid ,
119175 ip : u64 ,
120176 context : * mut c_void ) -> i32 {
121- let c: & mut & mut dyn FnMut ( & mut [ u8 ] , u64 , Asid ) -> i32
122- = mem:: transmute ( context) ;
123- c ( slice:: from_raw_parts_mut ( buffer, size) , ip, Asid ( * asid) )
177+ let buffer = std:: slice:: from_raw_parts_mut ( buffer, size) ;
178+ let asid = Asid ( * asid) ;
179+ BoxedCallback :: call ( context, buffer, ip, asid)
180+ }
181+
182+ /// Represent a boxed Rust function that can be passed to and from C code.
183+ ///
184+ /// # Internals
185+ ///
186+ /// The wrapped raw pointer points to the following structure on the heap:
187+ ///
188+ /// ```text
189+ /// ┌───────────────── Heap ──────────────────┐
190+ /// │ │
191+ /// │ ┌───────────────────────┼────►┌──────────┐
192+ /// │ │ │ │ │
193+ /// box_callback()────┼─►┌────────────┐ │ ┌───►┌────────────┐ │ │ vtable │
194+ /// │ │ │ │ │ │ │ │ │ │
195+ /// │ │ vtable ptr ├─┘ │ │ │ │ └──────────┘
196+ /// │ │ │ │ │ Captured │ │
197+ /// │ ├────────────┤ │ │ │ │
198+ /// │ │ │ │ │ Data │ │
199+ /// │ │ captures ├────┘ │ │ │
200+ /// │ │ │ │ │ │
201+ /// │ └────────────┘ └────────────┘ │
202+ /// │ │
203+ /// └─────────────────────────────────────────┘
204+ /// ```
205+ #[ derive( Debug , Eq , PartialEq ) ]
206+ #[ repr( transparent) ]
207+ struct BoxedCallback ( * mut c_void ) ;
208+
209+ impl BoxedCallback {
210+ /// Box the given Rust closure into a `BoxedCallback`.
211+ fn box_callback < F > ( callback : F ) -> Self
212+ where
213+ F : FnMut ( & mut [ u8 ] , u64 , Asid ) -> i32 ,
214+ {
215+ // The callback can be an arbitrary Rust closure. So move it onto the heap
216+ // (the allocation only takes place when the closure captures).
217+ let boxed_dyn_callback: Box < dyn FnMut ( & mut [ u8 ] , u64 , Asid ) -> i32 >
218+ = Box :: new ( callback) ;
219+
220+ // `boxed_dyn_callback` is itself a fat pointer and we cannot just return it.
221+ // Instead, move `boxed_dyn_callback` onto the heap and returns the pointer
222+ // to the fat pointer on heap.
223+ //
224+ // Note that we convert `boxed_dyn_callback` into raw pointer as below. This is
225+ // because `Box<T: ?Sized>` is not guaranteed to be ABI-compliant with `*T`.
226+ let boxed_ptr = Box :: new ( Box :: into_raw ( boxed_dyn_callback) ) ;
227+ let raw_ptr = Box :: into_raw ( boxed_ptr) as * mut c_void ;
228+ Self ( raw_ptr)
229+ }
230+
231+ /// Invoke the callback behind the given opaque boxed callback pointer.
232+ unsafe fn call ( opaque_cb : * mut c_void , buf : & mut [ u8 ] , size : u64 , asid : Asid ) -> i32 {
233+ let raw_boxed_ptr = opaque_cb
234+ as * mut * mut dyn FnMut ( & mut [ u8 ] , u64 , Asid ) -> i32 ;
235+ let func = ( * raw_boxed_ptr) . as_mut ( ) . unwrap ( ) ;
236+ func ( buf, size, asid)
237+ }
238+ }
239+
240+ impl Drop for BoxedCallback {
241+ fn drop ( & mut self ) {
242+ let raw_boxed_ptr = self . 0
243+ as * mut * mut dyn FnMut ( & mut [ u8 ] , u64 , Asid ) -> i32 ;
244+
245+ unsafe {
246+ // Drop from inside to outside.
247+ drop ( Box :: from ( * raw_boxed_ptr) ) ;
248+ drop ( Box :: from ( raw_boxed_ptr) ) ;
249+ }
250+ }
124251}
125252
126253/// An Image defines the memory image that was traced as a collection
@@ -129,7 +256,9 @@ pub struct Image<'a> {
129256 // the wrapped inst
130257 pub ( crate ) inner : & ' a mut pt_image ,
131258 // do we need to free this instance on drop?
132- dealloc : bool
259+ dealloc : bool ,
260+ // Any read data callback set by this `Image` instance.
261+ callback : Option < BoxedCallback > ,
133262}
134263
135264impl < ' a > Image < ' a > {
@@ -144,7 +273,7 @@ impl<'a> Image<'a> {
144273 PtErrorCode :: Invalid ,
145274 "invalid @name string: contains null bytes"
146275 ) ) ?. as_ptr ( ) )
147- } } ) . map ( |i| Image { inner : i, dealloc : true } )
276+ } } ) . map ( |i| Image { inner : i, dealloc : true , callback : None } )
148277 }
149278
150279 /// Get the image name.
@@ -198,12 +327,13 @@ impl<'a> Image<'a> {
198327 /// If @callback is None, the callback is removed.
199328 pub fn set_callback < F > ( & mut self , callback : Option < F > ) -> Result < ( ) , PtError >
200329 where F : FnMut ( & mut [ u8 ] , u64 , Asid ) -> i32 {
201- ensure_ptok ( unsafe { match callback {
330+ self . callback = callback. map ( BoxedCallback :: box_callback) ;
331+ ensure_ptok ( unsafe { match & self . callback {
202332 None => pt_image_set_callback ( self . inner , None , ptr:: null_mut ( ) ) ,
203- Some ( mut cb) =>
333+ Some ( cb) =>
204334 pt_image_set_callback ( self . inner ,
205335 Some ( read_callback) ,
206- & mut & mut cb as * mut _ as * mut c_void )
336+ cb . 0 )
207337 } } )
208338 }
209339
@@ -266,7 +396,7 @@ impl<'a> Image<'a> {
266396
267397impl < ' a > From < & ' a mut pt_image > for Image < ' a > {
268398 fn from ( img : & ' a mut pt_image ) -> Self {
269- Image { inner : img, dealloc : false }
399+ Image { inner : img, dealloc : false , callback : None }
270400 }
271401}
272402
0 commit comments