1
1
//! Implements calling functions from a native library.
2
2
3
+ use std:: io:: Write ;
3
4
use std:: ops:: Deref ;
4
5
5
6
use libffi:: low:: CodePtr ;
6
7
use libffi:: middle:: Type as FfiType ;
7
- use rustc_abi:: Size ;
8
+ use rustc_abi:: { HasDataLayout , Size } ;
8
9
use rustc_middle:: ty:: { self as ty, IntTy , Ty , UintTy } ;
9
10
use rustc_span:: Symbol ;
10
11
use serde:: { Deserialize , Serialize } ;
@@ -21,7 +22,7 @@ mod ffi;
21
22
) ]
22
23
pub mod trace;
23
24
24
- use self :: ffi:: { OwnedArg , ScalarArg } ;
25
+ use self :: ffi:: OwnedArg ;
25
26
use crate :: * ;
26
27
27
28
/// The final results of an FFI trace, containing every relevant event detected
@@ -77,7 +78,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
77
78
link_name : Symbol ,
78
79
dest : & MPlaceTy < ' tcx > ,
79
80
ptr : CodePtr ,
80
- libffi_args : & [ OwnedArg ] ,
81
+ libffi_args : & mut [ OwnedArg ] ,
81
82
) -> InterpResult < ' tcx , ( crate :: ImmTy < ' tcx > , Option < MemEvents > ) > {
82
83
let this = self . eval_context_mut ( ) ;
83
84
#[ cfg( target_os = "linux" ) ]
@@ -274,90 +275,122 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
274
275
/// and convert it to a `OwnedArg`.
275
276
fn op_to_ffi_arg ( & self , v : & OpTy < ' tcx > , tracing : bool ) -> InterpResult < ' tcx , OwnedArg > {
276
277
let this = self . eval_context_ref ( ) ;
277
- let scalar = |v| interp_ok ( this. read_immediate ( v) ?. to_scalar ( ) ) ;
278
- interp_ok ( match v. layout . ty . kind ( ) {
279
- // If the primitive provided can be converted to a type matching the type pattern
280
- // then create a `OwnedArg` of this primitive value with the corresponding `OwnedArg` constructor.
281
- // the ints
282
- ty:: Int ( IntTy :: I8 ) => ScalarArg :: Int8 ( scalar ( v) ?. to_i8 ( ) ?) . into ( ) ,
283
- ty:: Int ( IntTy :: I16 ) => ScalarArg :: Int16 ( scalar ( v) ?. to_i16 ( ) ?) . into ( ) ,
284
- ty:: Int ( IntTy :: I32 ) => ScalarArg :: Int32 ( scalar ( v) ?. to_i32 ( ) ?) . into ( ) ,
285
- ty:: Int ( IntTy :: I64 ) => ScalarArg :: Int64 ( scalar ( v) ?. to_i64 ( ) ?) . into ( ) ,
286
- ty:: Int ( IntTy :: Isize ) =>
287
- ScalarArg :: ISize ( scalar ( v) ?. to_target_isize ( this) ?. try_into ( ) . unwrap ( ) ) . into ( ) ,
288
- // the uints
289
- ty:: Uint ( UintTy :: U8 ) => ScalarArg :: UInt8 ( scalar ( v) ?. to_u8 ( ) ?) . into ( ) ,
290
- ty:: Uint ( UintTy :: U16 ) => ScalarArg :: UInt16 ( scalar ( v) ?. to_u16 ( ) ?) . into ( ) ,
291
- ty:: Uint ( UintTy :: U32 ) => ScalarArg :: UInt32 ( scalar ( v) ?. to_u32 ( ) ?) . into ( ) ,
292
- ty:: Uint ( UintTy :: U64 ) => ScalarArg :: UInt64 ( scalar ( v) ?. to_u64 ( ) ?) . into ( ) ,
293
- ty:: Uint ( UintTy :: Usize ) =>
294
- ScalarArg :: USize ( scalar ( v) ?. to_target_usize ( this) ?. try_into ( ) . unwrap ( ) ) . into ( ) ,
295
- ty:: RawPtr ( ..) => {
296
- let ptr = scalar ( v) ?. to_pointer ( this) ?;
297
- this. expose_and_warn ( ptr. provenance , tracing) ?;
298
-
299
- // This relies on the `expose_provenance` in the `visit_reachable_allocs` callback
300
- // below to expose the actual interpreter-level allocation.
301
- ScalarArg :: RawPtr ( std:: ptr:: with_exposed_provenance_mut ( ptr. addr ( ) . bytes_usize ( ) ) )
302
- . into ( )
278
+
279
+ // This should go first so that we emit unsupported before doing a bunch
280
+ // of extra work for types that aren't supported yet.
281
+ let ty = this. ty_to_ffitype ( v. layout . ty ) ?;
282
+
283
+ // Now grab the bytes of the argument.
284
+ let bytes = match v. as_mplace_or_imm ( ) {
285
+ either:: Either :: Left ( mplace) => {
286
+ // Get the alloc id corresponding to this mplace, alongside
287
+ // a pointer that's offset to point to this particular
288
+ // mplace (not one at the base addr of the allocation).
289
+ let mplace_ptr = mplace. ptr ( ) ;
290
+ let sz = mplace. layout . size . bytes_usize ( ) ;
291
+ if sz == 0 {
292
+ throw_unsup_format ! ( "Attempting to pass a ZST over FFI" ) ;
293
+ }
294
+ let ( id, ofs, _) = this. ptr_get_alloc_id ( mplace_ptr, sz. try_into ( ) . unwrap ( ) ) ?;
295
+ let ofs = ofs. bytes_usize ( ) ;
296
+ // Expose all provenances in the allocation within the byte
297
+ // range of the struct, if any.
298
+ let alloc = this. get_alloc_raw ( id) ?;
299
+ let alloc_ptr = this. get_alloc_bytes_unchecked_raw ( id) ?;
300
+ for prov in alloc. provenance ( ) . get_range ( this, ( ofs..ofs. strict_add ( sz) ) . into ( ) ) {
301
+ this. expose_provenance ( prov) ?;
302
+ }
303
+ // SAFETY: We know for sure that at alloc_ptr + ofs the next layout.size
304
+ // bytes are part of this allocation and initialised. They might be marked
305
+ // as uninit in Miri, but all bytes returned by `MiriAllocBytes` are
306
+ // initialised.
307
+ unsafe {
308
+ Box :: from ( std:: slice:: from_raw_parts (
309
+ alloc_ptr. add ( ofs) ,
310
+ mplace. layout . size . bytes_usize ( ) ,
311
+ ) )
312
+ }
303
313
}
304
- // For ADTs, create an FfiType from their fields.
305
- ty:: Adt ( adt_def, args) => {
306
- let adt = this. adt_to_ffitype ( v. layout . ty , * adt_def, args) ?;
307
-
308
- // Copy the raw bytes backing this arg.
309
- let bytes = match v. as_mplace_or_imm ( ) {
310
- either:: Either :: Left ( mplace) => {
311
- // Get the alloc id corresponding to this mplace, alongside
312
- // a pointer that's offset to point to this particular
313
- // mplace (not one at the base addr of the allocation).
314
- let mplace_ptr = mplace. ptr ( ) ;
315
- let sz = mplace. layout . size . bytes_usize ( ) ;
316
- let id = this
317
- . alloc_id_from_addr (
318
- mplace_ptr. addr ( ) . bytes ( ) ,
319
- sz. try_into ( ) . unwrap ( ) ,
320
- /* only_exposed_allocations */ false ,
321
- )
322
- . unwrap ( ) ;
323
- // Expose all provenances in the allocation within the byte
324
- // range of the struct, if any.
325
- let alloc = this. get_alloc_raw ( id) ?;
326
- let alloc_ptr = this. get_alloc_bytes_unchecked_raw ( id) ?;
327
- let start_addr =
328
- mplace_ptr. addr ( ) . bytes_usize ( ) . strict_sub ( alloc_ptr. addr ( ) ) ;
329
- for prov in alloc
330
- . provenance ( )
331
- . get_range ( this, ( start_addr..start_addr. strict_add ( sz) ) . into ( ) )
332
- {
333
- this. expose_provenance ( prov) ?;
314
+ either:: Either :: Right ( imm) => {
315
+ let ( first, maybe_second) = imm. to_scalar_and_meta ( ) ;
316
+ // If a scalar is a pointer, then expose its provenance.
317
+ if let interpret:: Scalar :: Ptr ( p, _) = first {
318
+ // This relies on the `expose_provenance` in the `visit_reachable_allocs` callback
319
+ // below to expose the actual interpreter-level allocation.
320
+ this. expose_and_warn ( Some ( p. provenance ) , tracing) ?;
321
+ }
322
+
323
+ // Turn the scalar(s) into u128s so we can write their bytes
324
+ // into the buffer.
325
+ let ( sc_int_first, sz_first) = {
326
+ let sc = first. to_scalar_int ( ) ?;
327
+ ( sc. to_bits_unchecked ( ) , sc. size ( ) . bytes_usize ( ) )
328
+ } ;
329
+ let ( sc_int_second, sz_second) = match maybe_second {
330
+ MemPlaceMeta :: Meta ( sc) => {
331
+ // Might also be a pointer.
332
+ if let interpret:: Scalar :: Ptr ( p, _) = first {
333
+ this. expose_and_warn ( Some ( p. provenance ) , tracing) ?;
334
334
}
335
- // SAFETY: We know for sure that at mplace_ptr.addr() the next layout.size
336
- // bytes are part of this allocation and initialised. They might be marked
337
- // as uninit in Miri, but all bytes returned by `MiriAllocBytes` are
338
- // initialised.
339
- unsafe {
340
- std:: slice:: from_raw_parts (
341
- alloc_ptr. with_addr ( mplace_ptr. addr ( ) . bytes_usize ( ) ) ,
342
- mplace. layout . size . bytes_usize ( ) ,
343
- )
344
- . to_vec ( )
345
- . into_boxed_slice ( )
335
+ let sc = sc. to_scalar_int ( ) ?;
336
+ ( sc. to_bits_unchecked ( ) , sc. size ( ) . bytes_usize ( ) )
337
+ }
338
+ MemPlaceMeta :: None => ( 0 , 0 ) ,
339
+ } ;
340
+ let sz = imm. layout . size . bytes_usize ( ) ;
341
+ // TODO: Is this actually ok? Seems like the only way to figure
342
+ // out how the scalars are laid out relative to each other.
343
+ let align_second = match imm. layout . backend_repr {
344
+ rustc_abi:: BackendRepr :: Scalar ( _) => 1 ,
345
+ rustc_abi:: BackendRepr :: ScalarPair ( _, sc2) => sc2. align ( this) . bytes_usize ( ) ,
346
+ _ => unreachable ! ( ) ,
347
+ } ;
348
+ // How many bytes to skip between scalars if necessary for alignment.
349
+ let skip = sz_first. next_multiple_of ( align_second) . strict_sub ( sz_first) ;
350
+
351
+ let mut bytes: Box < [ u8 ] > = ( 0 ..sz) . map ( |_| 0u8 ) . collect ( ) ;
352
+
353
+ // Copy over the bytes in an endianness-agnostic way. Since each
354
+ // scalar may be up to 128 bits and write_target_uint doesn't
355
+ // give us an easy way to do multiple writes in a row, we
356
+ // adapt its logic for two consecutive writes.
357
+ let mut bytes_wr = bytes. as_mut ( ) ;
358
+ match this. data_layout ( ) . endian {
359
+ rustc_abi:: Endian :: Little => {
360
+ // Only write as many bytes as specified, not all of the u128.
361
+ let wr = bytes_wr. write ( & sc_int_first. to_le_bytes ( ) [ ..sz_first] ) . unwrap ( ) ;
362
+ assert_eq ! ( wr, sz_first) ;
363
+ // If the second scalar is zeroed, it's more efficient to skip it.
364
+ if sc_int_second != 0 {
365
+ bytes_wr = bytes_wr. split_at_mut ( skip) . 1 ;
366
+ let wr =
367
+ bytes_wr. write ( & sc_int_second. to_le_bytes ( ) [ ..sz_second] ) . unwrap ( ) ;
368
+ assert_eq ! ( wr, sz_second) ;
346
369
}
347
370
}
348
- either:: Either :: Right ( _) => {
349
- // TODO: support this!
350
- throw_unsup_format ! (
351
- "Immediate structs can't be passed over FFI: {}" ,
352
- v. layout. ty
353
- )
371
+ rustc_abi:: Endian :: Big => {
372
+ // TODO: My gut says this is wrong, let's see if CI complains.
373
+ let wr = bytes_wr
374
+ . write ( & sc_int_first. to_be_bytes ( ) [ 16usize . strict_sub ( sz_first) ..] )
375
+ . unwrap ( ) ;
376
+ assert_eq ! ( wr, sz_first) ;
377
+ if sc_int_second != 0 {
378
+ bytes_wr = bytes_wr. split_at_mut ( skip) . 1 ;
379
+ let wr = bytes_wr
380
+ . write (
381
+ & sc_int_second. to_be_bytes ( ) [ 16usize . strict_sub ( sz_second) ..] ,
382
+ )
383
+ . unwrap ( ) ;
384
+ assert_eq ! ( wr, sz_second) ;
385
+ }
354
386
}
355
- } ;
387
+ }
388
+ // Any remaining bytes are padding, so ignore.
356
389
357
- ffi :: OwnedArg :: Adt ( adt , bytes)
390
+ bytes
358
391
}
359
- _ => throw_unsup_format ! ( "unsupported argument type for native call: {}" , v . layout . ty ) ,
360
- } )
392
+ } ;
393
+ interp_ok ( OwnedArg :: new ( ty , bytes ) )
361
394
}
362
395
363
396
/// Parses an ADT to construct the matching libffi type.
@@ -495,7 +528,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
495
528
496
529
// Call the function and store output, depending on return type in the function signature.
497
530
let ( ret, maybe_memevents) =
498
- this. call_native_with_args ( link_name, dest, code_ptr, & libffi_args) ?;
531
+ this. call_native_with_args ( link_name, dest, code_ptr, & mut libffi_args) ?;
499
532
500
533
if tracing {
501
534
this. tracing_apply_accesses ( maybe_memevents. unwrap ( ) ) ?;
0 commit comments