Skip to main content

relibc/header/dlfcn/
mod.rs

1//! `dlfcn.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html>.
4
5// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
6// dropped from src/lib.rs.
7#![warn(warnings, unused_variables)]
8
9use core::{
10    ptr, str,
11    sync::atomic::{AtomicUsize, Ordering},
12};
13
14use crate::{
15    c_str::CStr,
16    ld_so::{
17        linker::{DlError, ObjectHandle, Resolve, ScopeKind},
18        tcb::Tcb,
19    },
20    platform::types::{c_char, c_int, c_void},
21};
22
23/// Relocations are performed at an implementation-defined time.
24pub const RTLD_LAZY: c_int = 1 << 0;
25/// Relocations are performed when the object is loaded.
26pub const RTLD_NOW: c_int = 1 << 1;
27/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/dlopen.3.html>.
28/// Don't load the shared object.
29pub const RTLD_NOLOAD: c_int = 1 << 2;
30/// All symbols are available for relocation processing of other modules.
31pub const RTLD_GLOBAL: c_int = 1 << 8;
32/// All symbold are not made available for relocation processing by other
33/// modules.
34pub const RTLD_LOCAL: c_int = 0x0000;
35
36/// Special purpose value for `handle` for `dlsym()` reserved by POSIX for
37/// future use.
38/// The identifier lookup happens in the normal global scope; that is, a
39/// search for an identifier using `handle` would find the same definition
40/// as a direct use of this identifier in the program code.
41#[allow(clippy::zero_ptr)] // related cbindgen issue: https://github.com/mozilla/cbindgen/issues/948
42pub const RTLD_DEFAULT: *mut c_void = 0 as *mut c_void; // XXX: cbindgen doesn't like ptr::null_mut() for publically exported constants
43
44static ERROR_NOT_SUPPORTED: &core::ffi::CStr = c"dlfcn not supported";
45
46#[thread_local]
47static ERROR: AtomicUsize = AtomicUsize::new(0);
48
49fn set_last_error(error: DlError) {
50    ERROR.store(error.repr().as_ptr() as usize, Ordering::SeqCst);
51}
52
53/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html>.
54#[repr(C)]
55#[allow(non_camel_case_types)]
56pub struct Dl_info_t {
57    /// Pathname of mapped object file.
58    dli_fname: *const c_char,
59    /// Base of mapped address range.
60    dli_fbase: *mut c_void,
61    /// Symbol name or null pointer.
62    dli_sname: *const c_char,
63    /// Symbol address of null pointer.
64    dli_saddr: *mut c_void,
65}
66
67/// alias as per spec update: <https://www.austingroupbugs.net/view.php?id=1847>
68pub type Dl_info = Dl_info_t;
69
70/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dladdr.html>.
71#[unsafe(no_mangle)]
72pub unsafe extern "C" fn dladdr(_addr: *const c_void, info: *mut Dl_info_t) -> c_int {
73    //TODO
74    unsafe {
75        (*info).dli_fname = ptr::null();
76        (*info).dli_fbase = ptr::null_mut();
77        (*info).dli_sname = ptr::null();
78        (*info).dli_saddr = ptr::null_mut();
79    }
80    0
81}
82
83/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlopen.html>.
84#[unsafe(no_mangle)]
85pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut c_void {
86    //TODO support all sort of flags
87    let resolve = if flags & RTLD_NOW == RTLD_NOW {
88        Resolve::Now
89    } else {
90        Resolve::Lazy
91    };
92
93    let scope = if flags & RTLD_GLOBAL == RTLD_GLOBAL {
94        ScopeKind::Global
95    } else {
96        ScopeKind::Local
97    };
98
99    let noload = flags & RTLD_NOLOAD == RTLD_NOLOAD;
100
101    let filename = if cfilename.is_null() {
102        None
103    } else {
104        unsafe {
105            Some(str::from_utf8_unchecked(
106                CStr::from_ptr(cfilename).to_bytes(),
107            ))
108        }
109    };
110
111    let tcb = match unsafe { Tcb::current() } {
112        Some(tcb) => tcb,
113        None => {
114            ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
115            return ptr::null_mut();
116        }
117    };
118
119    if tcb.linker_ptr.is_null() {
120        ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
121        return ptr::null_mut();
122    }
123
124    let mut linker = unsafe { (*tcb.linker_ptr).lock() };
125
126    let cbs_c = linker.cbs.clone();
127    let cbs = cbs_c.borrow();
128
129    match (cbs.load_library)(&mut linker, filename, resolve, scope, noload) {
130        Ok(handle) => handle.as_ptr().cast_mut(),
131        Err(error) => {
132            set_last_error(error);
133            ptr::null_mut()
134        }
135    }
136}
137
138/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlsym.html>.
139#[unsafe(no_mangle)]
140pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void {
141    let handle = ObjectHandle::from_ptr(handle);
142
143    if symbol.is_null() {
144        ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
145        return ptr::null_mut();
146    }
147
148    let symbol_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes()) };
149
150    // FIXME(andypython): just call obj.scope.get_sym() directly or search the
151    // global scope.  The rest is unnecessary as Linker::get_sym() does not
152    // depend on the Linker state.
153    let tcb = match unsafe { Tcb::current() } {
154        Some(tcb) => tcb,
155        None => {
156            ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
157            return ptr::null_mut();
158        }
159    };
160
161    if tcb.linker_ptr.is_null() {
162        ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
163        return ptr::null_mut();
164    }
165
166    let linker = unsafe { (*tcb.linker_ptr).lock() };
167    let cbs_c = linker.cbs.clone();
168    let cbs = cbs_c.borrow();
169    match (cbs.get_sym)(&linker, handle, symbol_str) {
170        Some(sym) => sym,
171        _ => {
172            ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
173            ptr::null_mut()
174        }
175    }
176}
177
178/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlclose.html>.
179#[unsafe(no_mangle)]
180pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
181    let tcb = match unsafe { Tcb::current() } {
182        Some(tcb) => tcb,
183        None => {
184            ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
185            return -1;
186        }
187    };
188
189    if tcb.linker_ptr.is_null() {
190        ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
191        return -1;
192    };
193
194    let Some(handle) = ObjectHandle::from_ptr(handle) else {
195        set_last_error(DlError::InvalidHandle);
196        return -1;
197    };
198
199    let mut linker = unsafe { (*tcb.linker_ptr).lock() };
200    let cbs_c = linker.cbs.clone();
201    let cbs = cbs_c.borrow();
202    (cbs.unload)(&mut linker, handle);
203    0
204}
205
206/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlerror.html>.
207#[unsafe(no_mangle)]
208pub extern "C" fn dlerror() -> *mut c_char {
209    ERROR.swap(0, Ordering::SeqCst) as *mut c_char
210}