Skip to main content

fuchsia_dso/
lib.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub use fuchsia_dso_macro::main;
6
7use fidl::endpoints::ServerEnd;
8use fidl_fuchsia_process_lifecycle::LifecycleMarker;
9
10/// In a DSO component, perform setup necessary before calling `dso_main`.
11#[doc(hidden)]
12pub fn dso_init(
13    _handle_count: u32,
14    _handle: *mut ::zx::sys::zx_handle_t,
15    _handle_info: *mut u32,
16    _name_count: u32,
17    _names: *mut *const ::std::ffi::c_char,
18    _argc: ::std::ffi::c_int,
19    _argv: *mut *const ::std::ffi::c_char,
20    _envp: *mut *const ::std::ffi::c_char,
21) {
22    // TODO(https://fxbug.dev/403545512): implement this for sync support
23    //unsafe {
24    //fdio::__libc_extensions_init(handle_count, handle, handle_info, name_count, names);
25    //fdio::fdio_startup_handles_init_tls(handle_count, handle, handle_info);
26    //}
27    //fuchsia_dso::store_args(argc, argv, envp);
28}
29
30/// Arguments to an async DSO main()
31#[derive(Debug)]
32pub struct DsoAsyncArgs {
33    /// The component's incoming namespace.
34    pub incoming: Vec<cm_types::NamespaceEntry>,
35    /// The component's outgoing directory. This is an [`Option`] but should typically be present.
36    pub outgoing_dir: Option<fidl::endpoints::ServerEnd<::fidl_fuchsia_io::DirectoryMarker>>,
37    /// A reference to the dispatcher used to dispatch the component's tasks.
38    pub dispatcher: fdf::DispatcherRef<'static>,
39    /// The component's lifecycle server handle. It can close this channel to signal component
40    /// exit.
41    pub lifecycle: ServerEnd<LifecycleMarker>,
42    /// The component's structured config VMO, if it has one.
43    pub config: Option<zx::Vmo>,
44}
45
46/// Input type for [`dso_init_async`] that is `Send`.
47///
48/// # Safety
49///
50/// This type implements `Send`. It is the caller's responsibility not to cause race conditions
51/// with the pointers if they use `Send`.
52#[doc(hidden)]
53pub struct DsoStartAsyncPayload {
54    pub handle_count: u32,
55    pub handle: *mut ::zx::sys::zx_handle_t,
56    pub handle_info: *mut u32,
57    pub name_count: u32,
58    pub names: *mut *const ::std::ffi::c_char,
59    pub argc: ::std::ffi::c_int,
60    pub argv: *mut *const ::std::ffi::c_char,
61    pub envp: *mut *const ::std::ffi::c_char,
62    pub dispatcher: *mut ::std::ffi::c_void,
63}
64
65unsafe impl Send for DsoStartAsyncPayload {}
66
67/// In a DSO async component, perform setup necessary before calling `dso_main_async`.
68#[doc(hidden)]
69pub fn dso_init_async(payload: DsoStartAsyncPayload) -> DsoAsyncArgs {
70    use std::{ffi, ptr, slice};
71    let DsoStartAsyncPayload {
72        handle_count,
73        handle,
74        handle_info,
75        name_count,
76        names,
77        argc: _, // ignore for now
78        argv: _, // ignore for now
79        envp: _, // ignore for now
80        dispatcher,
81    } = payload;
82
83    // SAFETY: dso_runner which provides `handle` guarantees `handle_count` is in bounds.
84    let handle = unsafe { slice::from_raw_parts(handle, handle_count as usize) };
85    // SAFETY: dso_runner which provides `handle_info` guarantees `handle_count` is in bounds.
86    let handle_info = unsafe { slice::from_raw_parts(handle_info, handle_count as usize) };
87    // SAFETY: dso_runner which provides `names` guarantees `name_count` is in bounds.
88    let names = unsafe { slice::from_raw_parts(names, name_count as usize) };
89    let dispatcher = dispatcher as *mut fdf_sys::fdf_dispatcher_t;
90    // TODO(https://fxbug.dev/488394483): This is a test API but it's currently the simplest way to
91    // set the driver dispatcher on the fuchsia-async executor thread. This should be replaced
92    // when there's a better way to override the dispatcher.
93    // SAFETY: dso_runner guarantees `dispatcher` is a valid dispatcher.
94    assert_eq!(
95        unsafe { fdf_sys::fdf_testing_set_default_dispatcher(dispatcher) },
96        zx::sys::ZX_OK,
97        "fdf_testing_set_default_dispatcher"
98    );
99    // SAFETY: dso_runner guarantees `dispatcher` is an `fdf_dispatcher_t` so this is a valid cast.
100    let dispatcher = unsafe {
101        fdf::DispatcherRef::from_raw(ptr::NonNull::new(dispatcher).expect("null dispatcher"))
102    };
103    struct HandleInfo {
104        handle: zx::NullableHandle,
105        id: fuchsia_runtime::HandleInfo,
106    }
107    let handle_infos = handle.iter().zip(handle_info.iter()).filter_map(|(handle, info)| {
108        Some(HandleInfo {
109            id: fuchsia_runtime::HandleInfo::try_from(info.clone()).ok()?,
110            // SAFETY: dso_runner guarantees all handles in `handle_info` were valid.
111            handle: unsafe { zx::NullableHandle::from_raw(*handle) },
112        })
113    });
114
115    let mut incoming = vec![];
116    let mut outgoing_dir = None;
117    let mut lifecycle = None;
118    let mut config = None;
119    for handle_info in handle_infos {
120        let HandleInfo { id, handle } = handle_info;
121        match id.handle_type() {
122            fuchsia_runtime::HandleType::FileDescriptor => {
123                // TODO(https://fxbug.dev/403545512): what should we do with these?
124            }
125            fuchsia_runtime::HandleType::NamespaceDirectory => {
126                let arg = id.arg() as usize;
127                if arg >= names.len() {
128                    continue;
129                }
130                // SAFETY: dso_runner guarantees all names were valid C strings. We just checked
131                // that `arg` is in bounds.
132                let Ok(path) = unsafe { ffi::CStr::from_ptr(names[arg]) }.to_str() else {
133                    continue;
134                };
135                let Ok(path) = cm_types::NamespacePath::new(path) else {
136                    continue;
137                };
138                let directory = fidl::Channel::from(handle);
139                incoming.push(cm_types::NamespaceEntry { path, directory: directory.into() });
140            }
141            fuchsia_runtime::HandleType::DirectoryRequest => {
142                let directory = fidl::Channel::from(handle);
143                outgoing_dir = Some(directory.into());
144            }
145            fuchsia_runtime::HandleType::Lifecycle => {
146                let ch = fidl::Channel::from(handle);
147                lifecycle = Some(ServerEnd::<LifecycleMarker>::from(ch));
148            }
149            fuchsia_runtime::HandleType::ComponentConfigVmo => {
150                let vmo = zx::Vmo::from(handle);
151                config = Some(vmo);
152            }
153            _ => {}
154        }
155    }
156
157    let lifecycle = lifecycle.expect("libfuchsia: expected lifecycle handle missing");
158    DsoAsyncArgs { incoming, outgoing_dir, dispatcher, lifecycle, config }
159}
160
161/// In a DSO component, perform cleanup necessary after calling `dso_main`.
162#[doc(hidden)]
163pub fn dso_fini() {
164    // TODO(https://fxbug.dev/403545512): implement this for sync support
165    //unsafe {
166    //fdio::__libc_extensions_fini();
167    //}
168}
169
170#[doc(hidden)]
171pub fn adapt_to_pass_arguments<A, R>(f: impl FnOnce(A) -> R, args: A) -> impl FnOnce() -> R {
172    move || f(args)
173}