11//! Implements calling functions from a native library.
22
3+ use std:: io:: Write ;
34use std:: ops:: Deref ;
45
56use libffi:: low:: CodePtr ;
67use libffi:: middle:: Type as FfiType ;
7- use rustc_abi:: Size ;
8+ use rustc_abi:: { HasDataLayout , Size } ;
89use rustc_middle:: ty:: { self as ty, IntTy , Ty , UintTy } ;
910use rustc_span:: Symbol ;
1011use serde:: { Deserialize , Serialize } ;
@@ -21,7 +22,7 @@ mod ffi;
2122) ]
2223pub mod trace;
2324
24- use self :: ffi:: { OwnedArg , ScalarArg } ;
25+ use self :: ffi:: OwnedArg ;
2526use crate :: * ;
2627
2728/// The final results of an FFI trace, containing every relevant event detected
@@ -77,7 +78,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
7778 link_name : Symbol ,
7879 dest : & MPlaceTy < ' tcx > ,
7980 ptr : CodePtr ,
80- libffi_args : & [ OwnedArg ] ,
81+ libffi_args : & mut [ OwnedArg ] ,
8182 ) -> InterpResult < ' tcx , ( crate :: ImmTy < ' tcx > , Option < MemEvents > ) > {
8283 let this = self . eval_context_mut ( ) ;
8384 #[ cfg( target_os = "linux" ) ]
@@ -274,90 +275,122 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
274275 /// and convert it to a `OwnedArg`.
275276 fn op_to_ffi_arg ( & self , v : & OpTy < ' tcx > , tracing : bool ) -> InterpResult < ' tcx , OwnedArg > {
276277 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+ }
303313 }
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) ?;
334334 }
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) ;
346369 }
347370 }
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+ }
354386 }
355- } ;
387+ }
388+ // Any remaining bytes are padding, so ignore.
356389
357- ffi :: OwnedArg :: Adt ( adt , bytes)
390+ bytes
358391 }
359- _ => throw_unsup_format ! ( "unsupported argument type for native call: {}" , v . layout . ty ) ,
360- } )
392+ } ;
393+ interp_ok ( OwnedArg :: new ( ty , bytes ) )
361394 }
362395
363396 /// Parses an ADT to construct the matching libffi type.
@@ -495,7 +528,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
495528
496529 // Call the function and store output, depending on return type in the function signature.
497530 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) ?;
499532
500533 if tracing {
501534 this. tracing_apply_accesses ( maybe_memevents. unwrap ( ) ) ?;
0 commit comments