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}