Skip to main content

leodos_libcfs/os/
module.rs

1//! Safe, idiomatic wrappers for OSAL dynamic module loading APIs.
2//!
3//! This module provides a `Module` struct that is a safe RAII handle for a
4//! dynamically loaded shared library, ensuring it is properly unloaded when
5//! it goes out of scope.
6
7use crate::error::Result;
8use crate::ffi;
9use crate::os::util::c_name_from_str;
10use crate::os::util::c_path_from_str;
11use crate::os::util::path_from_c_buf;
12use crate::string_from_c_buf;
13use crate::status::check;
14use bitflags::bitflags;
15use core::ffi::c_void;
16use core::mem::MaybeUninit;
17use heapless::String;
18
19/// A type-safe, zero-cost wrapper for an OSAL Module ID.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[repr(transparent)]
22pub struct ModuleId(pub ffi::osal_id_t);
23
24bitflags! {
25    /// Flags that control the behavior of module loading.
26    #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27    pub struct ModuleFlags: u32 {
28        /// Symbols in the loaded module are added to the global symbol table.
29        const GLOBAL_SYMBOLS = ffi::OS_MODULE_FLAG_GLOBAL_SYMBOLS;
30        /// Symbols are kept local/private to this module.
31        const LOCAL_SYMBOLS = ffi::OS_MODULE_FLAG_LOCAL_SYMBOLS;
32    }
33}
34
35/// A high-level wrapper for module properties.
36#[derive(Debug, Clone)]
37pub struct ModuleProp {
38    /// The registered logical name of the module.
39    pub name: String<{ ffi::OS_MAX_API_NAME as usize }>,
40    /// The filename from which the module was loaded.
41    pub filename: String<{ ffi::OS_MAX_PATH_LEN as usize }>,
42    /// The entry point address of the module.
43    pub entry_point: usize,
44}
45
46/// A handle to a dynamically loaded OSAL module (shared library).
47///
48/// This is a wrapper around an `osal_id_t` that will automatically call
49/// `OS_ModuleUnload` when it goes out of scope, preventing resource leaks.
50#[derive(Debug)]
51pub struct Module {
52    id: ModuleId,
53}
54
55impl Module {
56    /// Loads a shared library object file into the running system.
57    ///
58    /// `GLOBAL_SYMBOLS` is the default; use `LOCAL_SYMBOLS` for
59    /// safer unloading, then use [`Module::symbol`] to look up
60    /// local symbols.
61    ///
62    /// # Arguments
63    /// * `name`: A unique logical name to identify the module.
64    /// * `filename`: The path to the object file to load (e.g., "/cf/my_lib.so").
65    /// * `flags`: Options for how symbols are loaded.
66    pub fn load(name: &str, filename: &str, flags: ModuleFlags) -> Result<Self> {
67        let c_name = c_name_from_str(name)?;
68        let c_filename = c_path_from_str(filename)?;
69        let mut module_id = MaybeUninit::uninit();
70
71        check(unsafe {
72            ffi::OS_ModuleLoad(
73                module_id.as_mut_ptr(),
74                c_name.as_ptr(),
75                c_filename.as_ptr(),
76                flags.bits(),
77            )
78        })?;
79
80        Ok(Self {
81            id: ModuleId(unsafe { module_id.assume_init() }),
82        })
83    }
84
85    /// Looks up the address of a symbol within this module.
86    ///
87    /// This is useful for finding function pointers in modules loaded with
88    /// `ModuleFlags::LOCAL_SYMBOLS`.
89    ///
90    /// # Safety
91    ///
92    /// Using the returned pointer is inherently unsafe. The caller must ensure
93    /// it is cast to the correct function pointer type and that the function
94    /// signature is correct. The symbol's lifetime is tied to the `Module` instance.
95    pub fn symbol(&self, name: &str) -> Result<*mut c_void> {
96        let c_name = c_name_from_str(name)?;
97        let mut symbol_addr = 0;
98        check(unsafe { ffi::OS_ModuleSymbolLookup(self.id.0, &mut symbol_addr, c_name.as_ptr()) })?;
99        Ok(symbol_addr as *mut c_void)
100    }
101
102    /// Returns the underlying `ModuleId`.
103    pub fn id(&self) -> ModuleId {
104        self.id
105    }
106
107    /// Retrieves information about this module.
108    pub fn info(&self) -> Result<ModuleProp> {
109        let mut prop = MaybeUninit::uninit();
110        check(unsafe { ffi::OS_ModuleInfo(self.id.0, prop.as_mut_ptr()) })?;
111        let prop = unsafe { prop.assume_init() };
112
113        Ok(ModuleProp {
114            name: string_from_c_buf(&prop.name)?,
115            filename: path_from_c_buf(&prop.filename)?,
116            entry_point: prop.entry_point,
117        })
118    }
119}
120
121impl Drop for Module {
122    /// Unloads the module when the `Module` object goes out of scope.
123    fn drop(&mut self) {
124        let _ = unsafe { ffi::OS_ModuleUnload(self.id.0) };
125    }
126}
127
128/// Dumps the system symbol table to the specified file.
129///
130/// Not all RTOS support this. Returns `OS_ERR_NOT_IMPLEMENTED`
131/// if not available.
132///
133/// # Arguments
134/// * `filename`: The path to the file to write the symbol table to.
135/// * `size_limit`: Maximum number of bytes to write.
136pub fn symbol_table_dump(filename: &str, size_limit: usize) -> Result<()> {
137    let c_filename = c_path_from_str(filename)?;
138    check(unsafe { ffi::OS_SymbolTableDump(c_filename.as_ptr(), size_limit) })?;
139    Ok(())
140}
141
142/// Looks up the address of a symbol in the global symbol table.
143///
144/// This can find symbols in the main executable or in any module loaded
145/// with `ModuleFlags::GLOBAL_SYMBOLS`.
146///
147/// # Safety
148/// Using the returned pointer is inherently unsafe. The caller must ensure
149/// it is cast to the correct function pointer type and that the function
150/// signature is correct. The lifetime of the symbol is not managed by this function.
151pub fn symbol_lookup(name: &str) -> Result<*mut c_void> {
152    let c_name = c_name_from_str(name)?;
153    let mut symbol_addr = 0;
154    check(unsafe { ffi::OS_SymbolLookup(&mut symbol_addr, c_name.as_ptr()) })?;
155    Ok(symbol_addr as *mut c_void)
156}