diff options
Diffstat (limited to 'components/rust/core/src/api/libloading.rs')
| -rw-r--r-- | components/rust/core/src/api/libloading.rs | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/components/rust/core/src/api/libloading.rs b/components/rust/core/src/api/libloading.rs new file mode 100644 index 0000000000..438f1fb40b --- /dev/null +++ b/components/rust/core/src/api/libloading.rs @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-10-14 foxglove libdl safe wrapper (libloading-like) + */ + +/*! Minimal `libloading`-style safe wrapper around RT-Thread libdl. +Provides RAII `Library` and typed `Symbol<T>` for dlsym. +*/ + +extern crate alloc; + +use core::ffi::{c_char, c_int, c_void}; +use core::ffi::CStr; +use core::marker::PhantomData; +use core::mem::forget; +use core::ops::Deref; + +use alloc::ffi::CString; +use alloc::vec::Vec; + +use crate::bindings::libc; + +pub use crate::bindings::libc::{RTLD_GLOBAL, RTLD_LAZY, RTLD_LOCAL, RTLD_NOW}; + +/* Error type for dynamic loading operations */ +pub enum DlError { + /* dlopen failed; contains last error C-string pointer */ + Open(*const c_char), + /* dlsym failed; contains last error C-string pointer */ + Symbol(*const c_char), + /* dlclose returned non-zero; contains return code */ + Close(c_int), +} + +impl core::fmt::Debug for DlError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Display::fmt(self, f) + } +} + +impl core::fmt::Display for DlError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + &DlError::Open(ptr) | &DlError::Symbol(ptr) => { + if ptr.is_null() { + write!(f, "[libdl open/sym error: none]") + } else { + unsafe { + // `ptr` is a raw C string pointer + let cstr = CStr::from_ptr(ptr); + match cstr.to_str() { + Ok(s) => write!(f, "{}", s), + Err(_) => write!(f, "[libdl error: invalid UTF-8]") + } + } + } + } + DlError::Close(rc) => write!(f, "dlclose rc={}", rc), + } + } +} + +/* RAII wrapper for a `dlopen` handle */ +pub struct Library { + handle: *mut c_void, +} + +impl Library { + /* Create a new library using default flags (`RTLD_NOW | RTLD_GLOBAL`). + * Accepts a Rust string path and converts internally to a C string. + */ + pub fn new(path: &str) -> Result<Self, DlError> { + let cstr = match CString::new(path) { + Ok(s) => s, + Err(_) => { + return Err(DlError::Open(core::ptr::null())); + } + }; + unsafe { Self::open_global(cstr.as_ptr()) } + } + /* Open a dynamic module with given flags + * Safety: `path` must be a valid, NUL-terminated C string pointer + */ + pub unsafe fn open(path: *const c_char, flags: c_int) -> Result<Self, DlError> { + let h = libc::dlopen(path, flags); + if h.is_null() { + Err(DlError::Open(libc::last_error_ptr())) + } else { + Ok(Library { handle: h }) + } + } + + /* Convenience open using `RTLD_NOW | RTLD_GLOBAL` */ + pub unsafe fn open_global(path: *const c_char) -> Result<Self, DlError> { + Self::open(path, RTLD_NOW | RTLD_GLOBAL) + } + + /* Resolve a typed symbol from the library using a C string pointer. + * Safety: Caller must ensure `T` matches the actual symbol type and `symbol` is NUL-terminated. + */ + pub unsafe fn get_ptr<T>(&self, symbol: *const c_char) -> Result<Symbol<'_, T>, DlError> { + let sym = libc::dlsym(self.handle, symbol); + if sym.is_null() { + Err(DlError::Symbol(libc::last_error_ptr())) + } else { + Ok(Symbol { + raw: sym, + _lib: PhantomData, + _ty: PhantomData, + }) + } + } + + /* Resolve a typed symbol from the library using a byte slice name. + * If the slice is not NUL-terminated, a temporary buffer is allocated. + */ + pub fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<'_, T>, DlError> { + let (ptr, _guard_buf): (*const c_char, Option<Vec<u8>>) = if symbol.last().copied() == Some(0) { + (symbol.as_ptr() as *const c_char, None) + } else { + let mut buf = Vec::with_capacity(symbol.len() + 1); + buf.extend_from_slice(symbol); + buf.push(0); + (buf.as_ptr() as *const c_char, Some(buf)) + }; + let sym = unsafe { libc::dlsym(self.handle, ptr) }; + if sym.is_null() { + Err(DlError::Symbol(libc::last_error_ptr())) + } else { + Ok(Symbol { + raw: sym, + _lib: PhantomData, + _ty: PhantomData, + }) + } + } + + /* Check if handle is null */ + pub fn is_null(&self) -> bool { + self.handle.is_null() + } + + /* Consume and close the library explicitly */ + pub unsafe fn close(self) -> Result<(), DlError> { + let h = self.handle; + forget(self); + let rc = libc::dlclose(h); + if rc == 1 { + Ok(()) + } else { + Err(DlError::Close(rc)) + } + } + + /* Leak the handle out (caller manages lifetime) */ + pub unsafe fn into_raw(self) -> *mut c_void { + let h = self.handle; + forget(self); + h + } +} + +impl Drop for Library { + fn drop(&mut self) { + unsafe { + if !self.handle.is_null() { + let _ = libc::dlclose(self.handle); + self.handle = core::ptr::null_mut(); + } + } + } +} + +/* RAII wrapper for a symbol tied to a `Library` lifetime. + * Stores the raw pointer and performs typed conversion at the use site. + */ +pub struct Symbol<'lib, T> { + raw: *mut c_void, + _lib: PhantomData<&'lib Library>, + _ty: PhantomData<T>, +} + +impl<'lib, T> Symbol<'lib, T> { + /* Check if the underlying pointer is null */ + pub fn is_null(&self) -> bool { + self.raw.is_null() + } + + /* Get the raw pointer as `*mut c_void` */ + pub fn as_raw(&self) -> *mut c_void { + self.raw + } + + /* Cast the raw pointer to a different pointer type + * Safety: Caller must ensure `U` is the correct target type. + */ + pub unsafe fn as_ptr<U>(&self) -> *mut U { + self.raw as *mut U + } + + /* Convert to the typed value (e.g., function pointer) at use site + Safety: `T` must be the correct type for the symbol. + */ + pub unsafe fn to_value(&self) -> T + where + T: Sized + Copy, + { + // Bitwise-copy the pointer value into a `T` without using `transmute`. + // This relies on `T` (e.g., extern "C" fn ...) having the same representation size. + let src: *const T = (&self.raw as *const *mut c_void) as *const T; + core::ptr::read(src) + } + + pub fn into_raw(self) -> *mut c_void { + let p = self.raw; + forget(self); + p + } +} + +impl<'lib, T> Deref for Symbol<'lib, T> { + type Target = T; + fn deref(&self) -> &Self::Target { + /* Reinterpret the `raw` pointer storage as a value of type `T`. */ + /* Safety: This is safe if `T` is a pointer-sized function pointer or compatible pointer type. */ + unsafe { &*((&self.raw as *const *mut c_void) as *const T) } + } +} + +#[inline] +pub fn last_error() -> *const c_char { + libc::last_error_ptr() +} |
