@@ -34,6 +34,62 @@ mod test {
34
34
// not much to test for in the unit tests
35
35
// the integration tests should have way more stuffs
36
36
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
+
37
93
#[ test]
38
94
fn test_img_alloc ( ) {
39
95
Image :: new ( None ) . unwrap ( ) ;
@@ -118,9 +174,80 @@ unsafe extern "C" fn read_callback(buffer: *mut u8,
118
174
asid : * const pt_asid ,
119
175
ip : u64 ,
120
176
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
+ }
124
251
}
125
252
126
253
/// An Image defines the memory image that was traced as a collection
@@ -129,7 +256,9 @@ pub struct Image<'a> {
129
256
// the wrapped inst
130
257
pub ( crate ) inner : & ' a mut pt_image ,
131
258
// 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 > ,
133
262
}
134
263
135
264
impl < ' a > Image < ' a > {
@@ -144,7 +273,7 @@ impl<'a> Image<'a> {
144
273
PtErrorCode :: Invalid ,
145
274
"invalid @name string: contains null bytes"
146
275
) ) ?. as_ptr ( ) )
147
- } } ) . map ( |i| Image { inner : i, dealloc : true } )
276
+ } } ) . map ( |i| Image { inner : i, dealloc : true , callback : None } )
148
277
}
149
278
150
279
/// Get the image name.
@@ -198,12 +327,13 @@ impl<'a> Image<'a> {
198
327
/// If @callback is None, the callback is removed.
199
328
pub fn set_callback < F > ( & mut self , callback : Option < F > ) -> Result < ( ) , PtError >
200
329
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 {
202
332
None => pt_image_set_callback ( self . inner , None , ptr:: null_mut ( ) ) ,
203
- Some ( mut cb) =>
333
+ Some ( cb) =>
204
334
pt_image_set_callback ( self . inner ,
205
335
Some ( read_callback) ,
206
- & mut & mut cb as * mut _ as * mut c_void )
336
+ cb . 0 )
207
337
} } )
208
338
}
209
339
@@ -266,7 +396,7 @@ impl<'a> Image<'a> {
266
396
267
397
impl < ' a > From < & ' a mut pt_image > for Image < ' a > {
268
398
fn from ( img : & ' a mut pt_image ) -> Self {
269
- Image { inner : img, dealloc : false }
399
+ Image { inner : img, dealloc : false , callback : None }
270
400
}
271
401
}
272
402
0 commit comments