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    // SAFETY: dso_runner guarantees `dispatcher` is an `fdf_dispatcher_t` so this is a valid cast.
91    let dispatcher = unsafe {
92        fdf::DispatcherRef::from_raw(ptr::NonNull::new(dispatcher).expect("null dispatcher"))
93    };
94    struct HandleInfo {
95        handle: zx::NullableHandle,
96        id: fuchsia_runtime::HandleInfo,
97    }
98    let handle_infos = handle.iter().zip(handle_info.iter()).filter_map(|(handle, info)| {
99        Some(HandleInfo {
100            id: fuchsia_runtime::HandleInfo::try_from(info.clone()).ok()?,
101            // SAFETY: dso_runner guarantees all handles in `handle_info` were valid.
102            handle: unsafe { zx::NullableHandle::from_raw(*handle) },
103        })
104    });
105
106    let mut incoming = vec![];
107    let mut outgoing_dir = None;
108    let mut lifecycle = None;
109    let mut config = None;
110    for handle_info in handle_infos {
111        let HandleInfo { id, handle } = handle_info;
112        match id.handle_type() {
113            fuchsia_runtime::HandleType::FileDescriptor => {
114                // TODO(https://fxbug.dev/403545512): what should we do with these?
115            }
116            fuchsia_runtime::HandleType::NamespaceDirectory => {
117                let arg = id.arg() as usize;
118                if arg >= names.len() {
119                    continue;
120                }
121                // SAFETY: dso_runner guarantees all names were valid C strings. We just checked
122                // that `arg` is in bounds.
123                let Ok(path) = unsafe { ffi::CStr::from_ptr(names[arg]) }.to_str() else {
124                    continue;
125                };
126                let Ok(path) = cm_types::NamespacePath::new(path) else {
127                    continue;
128                };
129                let directory = fidl::Channel::from(handle);
130                incoming.push(cm_types::NamespaceEntry { path, directory: directory.into() });
131            }
132            fuchsia_runtime::HandleType::DirectoryRequest => {
133                let directory = fidl::Channel::from(handle);
134                outgoing_dir = Some(directory.into());
135            }
136            fuchsia_runtime::HandleType::Lifecycle => {
137                let ch = fidl::Channel::from(handle);
138                lifecycle = Some(ServerEnd::<LifecycleMarker>::from(ch));
139            }
140            fuchsia_runtime::HandleType::ComponentConfigVmo => {
141                let vmo = zx::Vmo::from(handle);
142                config = Some(vmo);
143            }
144            _ => {}
145        }
146    }
147
148    let lifecycle = lifecycle.expect("libfuchsia: expected lifecycle handle missing");
149    DsoAsyncArgs { incoming, outgoing_dir, dispatcher, lifecycle, config }
150}
151
152/// In a DSO component, perform cleanup necessary after calling `dso_main`.
153#[doc(hidden)]
154pub fn dso_fini() {
155    // TODO(https://fxbug.dev/403545512): implement this for sync support
156    //unsafe {
157    //fdio::__libc_extensions_fini();
158    //}
159}
160
161#[doc(hidden)]
162pub fn adapt_to_pass_arguments<A, R>(f: impl FnOnce(A) -> R, args: A) -> impl FnOnce() -> R {
163    move || f(args)
164}