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