1
+ use std:: sync:: atomic:: { AtomicU64 , Ordering } ;
2
+
1
3
use rustc_middle:: ty:: { TyKind , TypeAndMut } ;
2
- use rustc_target:: abi:: { LayoutOf , Size } ;
4
+ use rustc_target:: abi:: { FieldsShape , LayoutOf , Size } ;
3
5
4
6
use crate :: stacked_borrows:: Tag ;
5
7
use crate :: * ;
@@ -399,15 +401,67 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>(
399
401
// bytes 0-3: reserved for signature on macOS
400
402
// (need to avoid this because it is set by static initializer macros)
401
403
// bytes 4-7: count of how many times this mutex has been locked, as a u32
402
- // bytes 12-15: mutex kind, as an i32
403
- // (the kind has to be at this offset for compatibility with static initializer macros)
404
+ // bytes 12-15 or 16-19 (depending on platform): mutex kind, as an i32
405
+ // (the kind has to be at its offset for compatibility with static initializer macros)
406
+
407
+ static LIBC_MUTEX_KIND_OFFSET_CACHE : AtomicU64 = AtomicU64 :: new ( 0 ) ;
408
+
409
+ fn libc_mutex_kind_offset < ' mir , ' tcx : ' mir > (
410
+ ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
411
+ ) -> InterpResult < ' tcx , u64 > {
412
+ // Check if this offset has already been found and memoized
413
+ let cached_value = LIBC_MUTEX_KIND_OFFSET_CACHE . load ( Ordering :: Relaxed ) ;
414
+ if cached_value != 0 {
415
+ return Ok ( cached_value) ;
416
+ }
417
+
418
+ // This function infers the offset of the `kind` field of libc's pthread_mutex_t
419
+ // C struct by examining the array inside libc::PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.
420
+ // At time of writing, it is always all zero bytes except for a one byte at one of
421
+ // four positions, depending on the target OS's C struct layout and the endianness of the
422
+ // target architecture. This offset will then be used in getters and setters below, so that
423
+ // mutexes created from static initializers can be emulated with the correct behavior.
424
+ let initializer_path = [ "libc" , "PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP" ] ;
425
+ let initializer_instance = ecx. resolve_path ( & initializer_path) ;
426
+ let initializer_cid = GlobalId { instance : initializer_instance, promoted : None } ;
427
+ let initializer_const_val = ecx. const_eval_raw ( initializer_cid) ?;
428
+ let array_mplacety = ecx. mplace_field ( initializer_const_val, 0 ) ?;
429
+ let array_length = match array_mplacety. layout . fields {
430
+ FieldsShape :: Array { count, .. } => count,
431
+ _ => bug ! ( "Couldn't get array length from type {:?}" , array_mplacety. layout. ty) ,
432
+ } ;
433
+
434
+ let kind_offset = if array_length < 20 {
435
+ bug ! ( "libc::PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP array was shorter than expected" ) ;
436
+ } else if ecx. read_scalar ( ecx. mplace_field ( array_mplacety, 16 ) ?. into ( ) ) ?. to_u8 ( ) ? != 0 {
437
+ // for little-endian architectures
438
+ 16
439
+ } else if ecx. read_scalar ( ecx. mplace_field ( array_mplacety, 19 ) ?. into ( ) ) ?. to_u8 ( ) ? != 0 {
440
+ // for big-endian architectures
441
+ // (note that the i32 spans bytes 16 through 19, so the offset of the kind field is 16)
442
+ 16
443
+ } else if ecx. read_scalar ( ecx. mplace_field ( array_mplacety, 12 ) ?. into ( ) ) ?. to_u8 ( ) ? != 0 {
444
+ // for little-endian architectures
445
+ 12
446
+ } else if ecx. read_scalar ( ecx. mplace_field ( array_mplacety, 15 ) ?. into ( ) ) ?. to_u8 ( ) ? != 0 {
447
+ // for big-endian architectures
448
+ // (note that the i32 spans bytes 12 through 15, so the offset of the kind field is 12)
449
+ 12
450
+ } else {
451
+ bug ! ( "Couldn't determine offset of `kind` in pthread_mutex_t" ) ;
452
+ } ;
453
+
454
+ // Save offset to memoization cache for future calls
455
+ LIBC_MUTEX_KIND_OFFSET_CACHE . store ( kind_offset, Ordering :: Relaxed ) ;
456
+ Ok ( kind_offset)
457
+ }
404
458
405
459
fn mutex_get_locked_count < ' mir , ' tcx : ' mir > (
406
460
ecx : & MiriEvalContext < ' mir , ' tcx > ,
407
461
mutex_op : OpTy < ' tcx , Tag > ,
408
462
) -> InterpResult < ' tcx , ScalarMaybeUndef < Tag > > {
409
463
// Ensure that the following read at an offset to the mutex pointer is within bounds
410
- assert_ptr_target_min_size ( ecx, mutex_op, 16 ) ?;
464
+ assert_ptr_target_min_size ( ecx, mutex_op, 20 ) ?;
411
465
let mutex_place = ecx. deref_operand ( mutex_op) ?;
412
466
let u32_layout = ecx. layout_of ( ecx. tcx . types . u32 ) ?;
413
467
let locked_count_place =
@@ -421,7 +475,7 @@ fn mutex_set_locked_count<'mir, 'tcx: 'mir>(
421
475
locked_count : impl Into < ScalarMaybeUndef < Tag > > ,
422
476
) -> InterpResult < ' tcx , ( ) > {
423
477
// Ensure that the following write at an offset to the mutex pointer is within bounds
424
- assert_ptr_target_min_size ( ecx, mutex_op, 16 ) ?;
478
+ assert_ptr_target_min_size ( ecx, mutex_op, 20 ) ?;
425
479
let mutex_place = ecx. deref_operand ( mutex_op) ?;
426
480
let u32_layout = ecx. layout_of ( ecx. tcx . types . u32 ) ?;
427
481
let locked_count_place =
@@ -430,15 +484,19 @@ fn mutex_set_locked_count<'mir, 'tcx: 'mir>(
430
484
}
431
485
432
486
fn mutex_get_kind < ' mir , ' tcx : ' mir > (
433
- ecx : & MiriEvalContext < ' mir , ' tcx > ,
487
+ ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
434
488
mutex_op : OpTy < ' tcx , Tag > ,
435
489
) -> InterpResult < ' tcx , ScalarMaybeUndef < Tag > > {
436
490
// Ensure that the following read at an offset to the mutex pointer is within bounds
437
- assert_ptr_target_min_size ( ecx, mutex_op, 16 ) ?;
491
+ assert_ptr_target_min_size ( ecx, mutex_op, 20 ) ?;
438
492
let mutex_place = ecx. deref_operand ( mutex_op) ?;
439
493
let i32_layout = ecx. layout_of ( ecx. tcx . types . i32 ) ?;
440
- let kind_place =
441
- mutex_place. offset ( Size :: from_bytes ( 12 ) , MemPlaceMeta :: None , i32_layout, ecx) ?;
494
+ let kind_place = mutex_place. offset (
495
+ Size :: from_bytes ( libc_mutex_kind_offset ( ecx) ?) ,
496
+ MemPlaceMeta :: None ,
497
+ i32_layout,
498
+ ecx,
499
+ ) ?;
442
500
ecx. read_scalar ( kind_place. into ( ) )
443
501
}
444
502
@@ -448,11 +506,15 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>(
448
506
kind : impl Into < ScalarMaybeUndef < Tag > > ,
449
507
) -> InterpResult < ' tcx , ( ) > {
450
508
// Ensure that the following write at an offset to the mutex pointer is within bounds
451
- assert_ptr_target_min_size ( ecx, mutex_op, 16 ) ?;
509
+ assert_ptr_target_min_size ( ecx, mutex_op, 20 ) ?;
452
510
let mutex_place = ecx. deref_operand ( mutex_op) ?;
453
511
let i32_layout = ecx. layout_of ( ecx. tcx . types . i32 ) ?;
454
- let kind_place =
455
- mutex_place. offset ( Size :: from_bytes ( 12 ) , MemPlaceMeta :: None , i32_layout, ecx) ?;
512
+ let kind_place = mutex_place. offset (
513
+ Size :: from_bytes ( libc_mutex_kind_offset ( ecx) ?) ,
514
+ MemPlaceMeta :: None ,
515
+ i32_layout,
516
+ ecx,
517
+ ) ?;
456
518
ecx. write_scalar ( kind. into ( ) , kind_place. into ( ) )
457
519
}
458
520
0 commit comments