1#![cfg_attr(not(test), no_std)]
2#![deny(unsafe_op_in_unsafe_fn)]
4
5use core::cell::{Cell, RefCell};
6use core::ffi::CStr;
7use core::mem::MaybeUninit;
8
9use crate::tls::ShimTlsVar;
10
11use linux_api::signal::{SigProcMaskAction, rt_sigprocmask};
12use num_enum::{IntoPrimitive, TryFromPrimitive};
13use shadow_shim_helper_rs::ipc::IPCData;
14use shadow_shim_helper_rs::shim_event::{ShimEventStartReq, ShimEventToShadow, ShimEventToShim};
15use shadow_shim_helper_rs::shim_shmem::{HostShmem, ManagerShmem, ProcessShmem, ThreadShmem};
16use shadow_shim_helper_rs::simulation_time::SimulationTime;
17use shadow_shim_helper_rs::syscall_types::ForeignPtr;
18use shadow_shmem::allocator::{ShMemBlockAlias, ShMemBlockSerialized, shdeserialize};
19use tls::ThreadLocalStorage;
20use vasi_sync::lazy_lock::LazyLock;
21use vasi_sync::scmutex::SelfContainedMutex;
22
23mod bindings {
25 #![allow(unused)]
26 #![allow(non_upper_case_globals)]
27 #![allow(non_camel_case_types)]
28 #![allow(non_snake_case)]
29 #![allow(improper_ctypes)]
31 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
32}
33
34pub mod clone;
35pub mod mmap_box;
36pub mod preempt;
37pub mod reinit_auxvec_random;
38pub mod shimlogger;
39pub mod syscall;
40pub mod tls;
41
42pub use shimlogger::export as shimlogger_export;
43
44pub mod signals;
45
46pub fn simtime() -> Option<SimulationTime> {
47 SimulationTime::from_c_simtime(unsafe { bindings::shim_sys_get_simtime_nanos() })
48}
49
50#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
57#[repr(u8)]
58pub enum ExecutionContext {
59 Shadow = EXECUTION_CONTEXT_SHADOW_CONST,
61 Application = EXECUTION_CONTEXT_APPLICATION_CONST,
62}
63
64pub const EXECUTION_CONTEXT_SHADOW_CONST: u8 = 0;
65pub const EXECUTION_CONTEXT_APPLICATION_CONST: u8 = 1;
66
67static CURRENT_EXECUTION_CONTEXT: ShimTlsVar<Cell<ExecutionContext>> =
70 ShimTlsVar::new(&SHIM_TLS, || Cell::new(ExecutionContext::Application));
71
72impl ExecutionContext {
73 pub fn current() -> ExecutionContext {
75 CURRENT_EXECUTION_CONTEXT.get().get()
76 }
77
78 pub fn enter(&self) -> ExecutionContextRestorer {
81 ExecutionContextRestorer {
82 prev: self.enter_without_restorer(),
83 }
84 }
85
86 pub fn enter_without_restorer(&self) -> ExecutionContext {
89 let current_execution_ctx = CURRENT_EXECUTION_CONTEXT.get();
90 let peeked_prev = current_execution_ctx.get();
91
92 let replaced_prev = match (peeked_prev, *self) {
96 (ExecutionContext::Shadow, ExecutionContext::Application) => {
97 unsafe { preempt::enable() };
104 current_execution_ctx.replace(*self)
105 }
106 (ExecutionContext::Application, ExecutionContext::Shadow) => {
107 let c = current_execution_ctx.replace(*self);
110 preempt::disable();
111 c
112 }
113 (ExecutionContext::Application, ExecutionContext::Application) => {
114 ExecutionContext::Application
116 }
117 (ExecutionContext::Shadow, ExecutionContext::Shadow) => {
118 ExecutionContext::Shadow
120 }
121 };
122 assert_eq!(peeked_prev, replaced_prev);
126 peeked_prev
127 }
128}
129
130#[must_use]
132#[derive(Debug)]
133pub struct ExecutionContextRestorer {
134 prev: ExecutionContext,
135}
136
137impl ExecutionContextRestorer {
138 pub fn ctx(&self) -> ExecutionContext {
140 self.prev
141 }
142}
143
144impl Drop for ExecutionContextRestorer {
145 fn drop(&mut self) {
146 ExecutionContext::enter_without_restorer(&self.prev);
147 }
148}
149
150const SHIM_SIGNAL_STACK_GUARD_OVERHEAD: usize = 4096 * 2;
153
154mod tls_thread_signal_stack {
155 use super::*;
156
157 static THREAD_SIGNAL_STACK: ShimTlsVar<Cell<*mut core::ffi::c_void>> =
158 ShimTlsVar::new(&SHIM_TLS, || Cell::new(core::ptr::null_mut()));
159
160 const SHIM_SIGNAL_STACK_MIN_USABLE_SIZE: usize = 1024 * 100;
169 const SHIM_SIGNAL_STACK_SIZE: usize =
170 SHIM_SIGNAL_STACK_GUARD_OVERHEAD + SHIM_SIGNAL_STACK_MIN_USABLE_SIZE;
171
172 pub fn init() {
179 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
180 if THREAD_SIGNAL_STACK.get().get().is_null() {
181 let new_stack = unsafe {
183 rustix::mm::mmap_anonymous(
184 core::ptr::null_mut(),
185 SHIM_SIGNAL_STACK_SIZE,
186 rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
187 rustix::mm::MapFlags::PRIVATE,
188 )
189 }
190 .unwrap();
191
192 assert!(
194 THREAD_SIGNAL_STACK.get().replace(new_stack).is_null(),
195 "Allocated signal stack twice for current thread"
196 );
197
198 unsafe { rustix::mm::mprotect(new_stack, 4096, rustix::mm::MprotectFlags::empty()) }
200 .unwrap();
201 } else {
202 }
207
208 let stack_descriptor = linux_api::signal::stack_t {
211 ss_sp: THREAD_SIGNAL_STACK.get().get(),
212 ss_size: SHIM_SIGNAL_STACK_SIZE.try_into().unwrap(),
213 ss_flags: linux_api::signal::SigAltStackFlags::SS_AUTODISARM.bits(),
219 };
220 unsafe {
221 linux_api::signal::sigaltstack(Some(&stack_descriptor), None).unwrap();
222 }
223 }
224
225 pub unsafe fn free() {
238 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
239 static FREE_SIGNAL_STACK: SelfContainedMutex<usize> = SelfContainedMutex::const_new(0);
247
248 let mut free_signal_stack = FREE_SIGNAL_STACK.lock();
249 let this_thread_stack = THREAD_SIGNAL_STACK.get().replace(core::ptr::null_mut());
250 let stack_to_free_now =
251 core::mem::replace(&mut *free_signal_stack, this_thread_stack as usize);
252 if stack_to_free_now != 0 {
253 unsafe {
254 rustix::mm::munmap(
255 stack_to_free_now as *mut core::ffi::c_void,
256 SHIM_SIGNAL_STACK_SIZE,
257 )
258 }
259 .unwrap();
260 }
261 }
262}
263
264mod tls_ipc {
267 use super::*;
268 static IPC_DATA_BLOCK: ShimTlsVar<RefCell<Option<ShMemBlockAlias<IPCData>>>> =
269 ShimTlsVar::new(&SHIM_TLS, || RefCell::new(None));
270
271 pub fn with<O>(f: impl FnOnce(&IPCData) -> O) -> O {
273 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
274 let ipc = IPC_DATA_BLOCK.get();
275 let ipc = ipc.borrow();
276 ipc.as_ref().map(|block| f(block)).unwrap()
277 }
278
279 pub unsafe fn set(blk: &ShMemBlockSerialized) {
286 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
287 let blk: ShMemBlockAlias<IPCData> = unsafe { shdeserialize(blk) };
288 IPC_DATA_BLOCK.get().replace(Some(blk));
289 }
290}
291
292mod tls_thread_shmem {
293 use super::*;
294
295 static SHMEM: ShimTlsVar<RefCell<Option<ShMemBlockAlias<ThreadShmem>>>> =
296 ShimTlsVar::new(&SHIM_TLS, || RefCell::new(None));
297
298 pub fn with<O>(f: impl FnOnce(&ThreadShmem) -> O) -> O {
300 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
301 f(SHMEM.get().borrow().as_ref().unwrap())
302 }
303
304 pub unsafe fn set(blk: &ShMemBlockSerialized) {
311 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
312 let blk = unsafe { shdeserialize(blk) };
314 SHMEM.get().borrow_mut().replace(blk);
315 }
316}
317
318mod global_manager_shmem {
319 use super::*;
320
321 static INITIALIZER: SelfContainedMutex<Option<ShMemBlockSerialized>> =
323 SelfContainedMutex::const_new(None);
324
325 static SHMEM: LazyLock<ShMemBlockAlias<ManagerShmem>> = LazyLock::const_new(|| {
328 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
329 let serialized = INITIALIZER.lock().take().unwrap();
330 unsafe { shdeserialize(&serialized) }
331 });
332
333 pub unsafe fn set(blk: &ShMemBlockSerialized) {
338 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
339 assert!(!SHMEM.initd());
340 assert!(INITIALIZER.lock().replace(*blk).is_none());
341 SHMEM.force();
345 }
346
347 pub fn get() -> impl core::ops::Deref<Target = ShMemBlockAlias<'static, ManagerShmem>> + 'static
349 {
350 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
351 SHMEM.force()
352 }
353
354 pub fn try_get()
355 -> Option<impl core::ops::Deref<Target = ShMemBlockAlias<'static, ManagerShmem>> + 'static>
356 {
357 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
358 if !SHMEM.initd() {
359 None
362 } else {
363 Some(get())
364 }
365 }
366}
367
368mod global_host_shmem {
369 use super::*;
370
371 static INITIALIZER: SelfContainedMutex<Option<ShMemBlockSerialized>> =
373 SelfContainedMutex::const_new(None);
374
375 static SHMEM: LazyLock<ShMemBlockAlias<HostShmem>> = LazyLock::const_new(|| {
378 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
379 let serialized = INITIALIZER.lock().take().unwrap();
380 unsafe { shdeserialize(&serialized) }
381 });
382
383 pub unsafe fn set(blk: &ShMemBlockSerialized) {
388 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
389 assert!(!SHMEM.initd());
390 assert!(INITIALIZER.lock().replace(*blk).is_none());
391 SHMEM.force();
395 }
396
397 pub fn get() -> impl core::ops::Deref<Target = ShMemBlockAlias<'static, HostShmem>> + 'static {
399 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
400 SHMEM.force()
401 }
402
403 pub fn try_get()
404 -> Option<impl core::ops::Deref<Target = ShMemBlockAlias<'static, HostShmem>> + 'static> {
405 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
406 if !SHMEM.initd() {
407 None
410 } else {
411 Some(get())
412 }
413 }
414}
415
416mod tls_process_shmem {
417 use super::*;
418
419 static SHMEM: ShimTlsVar<RefCell<Option<ShMemBlockAlias<ProcessShmem>>>> =
420 ShimTlsVar::new(&SHIM_TLS, || RefCell::new(None));
421
422 pub fn with<O>(f: impl FnOnce(&ProcessShmem) -> O) -> O {
424 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
425 f(SHMEM.get().borrow().as_ref().unwrap())
426 }
427
428 pub unsafe fn set(blk: &ShMemBlockSerialized) {
435 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
436 let blk = unsafe { shdeserialize(blk) };
438 SHMEM.get().borrow_mut().replace(blk);
439 }
440}
441
442extern crate log_c2rust;
446extern crate logger;
447extern crate shadow_shim_helper_rs;
448extern crate shadow_shmem;
449extern crate shadow_tsc;
450
451static SHIM_TLS: ThreadLocalStorage = unsafe { ThreadLocalStorage::new(tls::Mode::Native) };
456
457pub unsafe fn release_and_exit_current_thread(exit_status: i32) -> ! {
469 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
470 rt_sigprocmask(
473 SigProcMaskAction::SIG_BLOCK,
474 &linux_api::signal::sigset_t::FULL,
475 None,
476 )
477 .unwrap();
478
479 unsafe { SHIM_TLS.unregister_current_thread() }
482
483 linux_api::exit::exit_raw(exit_status).unwrap();
484 unreachable!()
485}
486
487extern "C" fn init_thread() {
494 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
495 unsafe { bindings::_shim_child_thread_init_preload() };
496 log::trace!("Finished shim thread init");
497}
498
499fn init_process() {
503 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
504 static STARTED_INIT: LazyLock<()> = LazyLock::const_new(|| ());
505 if STARTED_INIT.initd() {
506 return;
511 }
512 STARTED_INIT.force();
513
514 unsafe { bindings::_shim_parent_init_preload() };
515 log::trace!("Finished shim global init");
516}
517
518fn wait_for_start_event(is_first_thread: bool) {
522 debug_assert_eq!(ExecutionContext::current(), ExecutionContext::Shadow);
523 log::trace!("waiting for start event");
524
525 let mut working_dir = [0u8; linux_api::limits::PATH_MAX];
526 let working_dir_ptr;
527 let working_dir_len;
528 if is_first_thread {
529 working_dir_ptr = ForeignPtr::from_raw_ptr(working_dir.as_mut_ptr());
530 working_dir_len = working_dir.len();
531 } else {
532 working_dir_ptr = ForeignPtr::null();
533 working_dir_len = 0;
534 }
535
536 let mut thread_blk_serialized = MaybeUninit::<ShMemBlockSerialized>::uninit();
537 let mut process_blk_serialized = MaybeUninit::<ShMemBlockSerialized>::uninit();
538 let start_req = ShimEventToShadow::StartReq(ShimEventStartReq {
539 thread_shmem_block_to_init: ForeignPtr::from_raw_ptr(thread_blk_serialized.as_mut_ptr()),
540 process_shmem_block_to_init: ForeignPtr::from_raw_ptr(process_blk_serialized.as_mut_ptr()),
541 initial_working_dir_to_init: working_dir_ptr,
542 initial_working_dir_to_init_len: working_dir_len,
543 });
544 let res = tls_ipc::with(|ipc| {
545 ipc.to_shadow().send(start_req);
546 ipc.from_shadow().receive().unwrap()
547 });
548 let ShimEventToShim::StartRes(res) = res else {
549 panic!("Unexpected response: {res:?}");
550 };
551 if is_first_thread {
552 unsafe { reinit_auxvec_random::reinit_auxvec_random(&res.auxvec_random) };
561 }
562
563 let thread_blk_serialized = unsafe { thread_blk_serialized.assume_init() };
565 unsafe { tls_thread_shmem::set(&thread_blk_serialized) };
567
568 let process_blk_serialized = unsafe { process_blk_serialized.assume_init() };
570 unsafe { tls_process_shmem::set(&process_blk_serialized) };
572
573 if is_first_thread {
577 let working_dir = CStr::from_bytes_until_nul(&working_dir).unwrap();
578 rustix::process::chdir(working_dir).unwrap();
579 }
580}
581
582pub mod export {
587 use core::ops::Deref;
588
589 use super::*;
590
591 #[unsafe(no_mangle)]
595 pub unsafe extern "C-unwind" fn shim_api_syscall(
596 n: core::ffi::c_long,
597 arg1: u64,
598 arg2: u64,
599 arg3: u64,
600 arg4: u64,
601 arg5: u64,
602 arg6: u64,
603 ) -> i64 {
604 let _prev = ExecutionContext::Shadow.enter();
605 unsafe {
606 bindings::shimc_api_syscall(_prev.ctx().into(), n, arg1, arg2, arg3, arg4, arg5, arg6)
607 }
608 }
609
610 #[unsafe(no_mangle)]
614 pub unsafe extern "C-unwind" fn shim_api_getaddrinfo(
615 node: *const core::ffi::c_char,
616 service: *const core::ffi::c_char,
617 hints: *const libc::addrinfo,
618 res: *mut *mut libc::addrinfo,
619 ) -> i32 {
620 let _prev = ExecutionContext::Shadow.enter();
621 unsafe { bindings::shimc_api_getaddrinfo(node, service, hints, res) }
622 }
623
624 #[unsafe(no_mangle)]
629 pub unsafe extern "C-unwind" fn shim_api_freeaddrinfo(res: *mut libc::addrinfo) {
630 let _prev = ExecutionContext::Shadow.enter();
631 unsafe { bindings::shimc_api_freeaddrinfo(res) }
632 }
633
634 #[unsafe(no_mangle)]
638 pub unsafe extern "C-unwind" fn shim_api_getifaddrs(ifap: *mut *mut libc::ifaddrs) -> i32 {
639 unsafe { bindings::shimc_api_getifaddrs(ifap) }
644 }
645
646 #[unsafe(no_mangle)]
651 pub unsafe extern "C-unwind" fn shim_api_freeifaddrs(ifa: *mut libc::ifaddrs) {
652 unsafe { bindings::shimc_api_freeifaddrs(ifa) }
657 }
658
659 #[unsafe(no_mangle)]
665 pub extern "C-unwind" fn shim_swapExecutionContext(new: ExecutionContext) -> ExecutionContext {
666 new.enter_without_restorer()
667 }
668
669 #[unsafe(no_mangle)]
671 pub extern "C-unwind" fn shim_getExecutionContext() -> ExecutionContext {
672 ExecutionContext::current()
673 }
674
675 #[unsafe(no_mangle)]
683 pub extern "C-unwind" fn _shim_init_signal_stack() {
684 tls_thread_signal_stack::init();
685 }
686
687 #[unsafe(no_mangle)]
698 pub unsafe extern "C-unwind" fn shim_freeSignalStack() {
699 unsafe { tls_thread_signal_stack::free() };
700 }
701
702 #[unsafe(no_mangle)]
707 pub unsafe extern "C-unwind" fn _shim_parent_init_ipc() {
708 let mut bytes = [0; core::mem::size_of::<ShMemBlockSerialized>()];
709 let bytes_read = rustix::io::read(
710 unsafe { rustix::fd::BorrowedFd::borrow_raw(libc::STDIN_FILENO) },
711 &mut bytes,
712 )
713 .unwrap();
714 assert_eq!(bytes_read, bytes.len());
716 let ipc_blk = shadow_pod::from_array(&bytes);
717 unsafe { tls_ipc::set(&ipc_blk) };
719 }
720
721 #[unsafe(no_mangle)]
727 pub unsafe extern "C-unwind" fn shim_thisThreadEventIPC() -> *const IPCData {
728 tls_ipc::with(core::ptr::from_ref)
729 }
730
731 #[unsafe(no_mangle)]
737 pub unsafe extern "C-unwind" fn shim_threadSharedMem()
738 -> *const shadow_shim_helper_rs::shim_shmem::export::ShimShmemThread {
739 tls_thread_shmem::with(core::ptr::from_ref)
740 }
741
742 #[unsafe(no_mangle)]
743 pub extern "C-unwind" fn _shim_load() {
744 init_process();
745 }
746
747 #[unsafe(no_mangle)]
755 pub unsafe extern "C-unwind" fn shim_release_and_exit_current_thread(status: i32) {
756 unsafe { release_and_exit_current_thread(status) }
757 }
758
759 #[unsafe(no_mangle)]
760 pub extern "C-unwind" fn shim_managerSharedMem()
761 -> *const shadow_shim_helper_rs::shim_shmem::export::ShimShmemManager {
762 let rv = global_manager_shmem::try_get();
763 rv.map(|x| {
764 let rv: &shadow_shim_helper_rs::shim_shmem::export::ShimShmemManager = x.deref();
765 core::ptr::from_ref(rv)
769 })
770 .unwrap_or(core::ptr::null())
771 }
772
773 #[unsafe(no_mangle)]
774 pub extern "C-unwind" fn shim_hostSharedMem()
775 -> *const shadow_shim_helper_rs::shim_shmem::export::ShimShmemHost {
776 let rv = global_host_shmem::try_get();
777 rv.map(|x| {
778 let rv: &shadow_shim_helper_rs::shim_shmem::export::ShimShmemHost = x.deref();
779 core::ptr::from_ref(rv)
783 })
784 .unwrap_or(core::ptr::null())
785 }
786
787 #[unsafe(no_mangle)]
788 pub extern "C-unwind" fn shim_processSharedMem()
789 -> *const shadow_shim_helper_rs::shim_shmem::export::ShimShmemProcess {
790 tls_process_shmem::with(|process| {
791 core::ptr::from_ref(process)
795 })
796 }
797
798 #[unsafe(no_mangle)]
800 pub extern "C-unwind" fn _shim_preload_only_child_ipc_wait_for_start_event() {
801 wait_for_start_event(false);
802 }
803
804 #[unsafe(no_mangle)]
805 pub extern "C-unwind" fn _shim_ipc_wait_for_start_event() {
806 wait_for_start_event(true);
807 }
808
809 #[unsafe(no_mangle)]
810 pub extern "C-unwind" fn _shim_parent_init_manager_shm() {
811 unsafe { global_manager_shmem::set(&global_host_shmem::get().manager_shmem) }
812 }
813
814 #[unsafe(no_mangle)]
815 pub extern "C-unwind" fn _shim_parent_init_host_shm() {
816 tls_process_shmem::with(|process| unsafe { global_host_shmem::set(&process.host_shmem) });
817 }
818
819 #[unsafe(no_mangle)]
820 pub extern "C-unwind" fn _shim_parent_close_stdin() {
821 unsafe { rustix::io::close(libc::STDIN_FILENO) };
822 }
823}