dynamic_loader_cache/
lib.rs1#![doc = include_str!("../README.md")]
8#![doc(html_root_url = "https://docs.rs/dynamic-loader-cache/0.2.3")]
9#![warn(
10 unsafe_op_in_unsafe_fn,
11 missing_docs,
12 keyword_idents,
13 macro_use_extern_crate,
14 missing_debug_implementations,
15 non_ascii_idents,
16 trivial_casts,
17 trivial_numeric_casts,
18 unstable_features,
19 unused_extern_crates,
20 unused_import_braces,
21 unused_labels,
22 variant_size_differences,
23 unused_qualifications,
24 clippy::must_use_candidate,
25 clippy::default_numeric_fallback,
26 clippy::single_char_lifetime_names,
27 clippy::alloc_instead_of_core,
28 clippy::std_instead_of_core,
29 clippy::std_instead_of_alloc
30)]
31mod errors;
69pub mod glibc_ld_so_cache_1dot1;
70pub mod ld_elf_so_hints;
71pub mod ld_so_1dot7;
72pub mod ld_so_hints;
73#[cfg(test)]
74mod tests;
75mod utils;
76
77extern crate alloc;
78
79use alloc::borrow::Cow;
80use alloc::fmt;
81use core::ffi::CStr;
82use core::iter::FusedIterator;
83use core::mem::size_of;
84use std::ffi::OsStr;
85use std::path::Path;
86
87use arrayvec::ArrayVec;
88use static_assertions::const_assert;
89
90pub use crate::errors::Error;
91
92const CACHE_IMPL_COUNT: usize = 5;
93
94pub type Result<T> = core::result::Result<T, Error>;
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
100enum DataModel {
101 ILP32,
103 LP64,
105}
106
107#[derive(Debug)]
109#[non_exhaustive]
110pub struct Entry<'cache> {
111 pub file_name: Cow<'cache, OsStr>,
113 pub full_path: Cow<'cache, Path>,
115}
116
117impl<'cache> Entry<'cache> {
118 pub(crate) fn new_by_string_table_indices(
119 string_table: &utils::CStringTable<'cache>,
120 key: u32,
121 value: u32,
122 ) -> Result<Self> {
123 let key = string_table.get(key)?;
124 let value = string_table.get(value)?;
125
126 Self::cstr_entry_to_crate_entry(key, value)
127 }
128
129 #[cfg(unix)]
130 fn cstr_entry_to_crate_entry(key: &'cache CStr, value: &'cache CStr) -> Result<Self> {
131 let file_name = utils::os_str_from_cstr(key).map(Cow::Borrowed)?;
132 let full_path = utils::path_from_cstr(value).map(Cow::Borrowed)?;
133
134 Ok(Self {
135 file_name,
136 full_path,
137 })
138 }
139
140 #[cfg(not(unix))]
141 fn cstr_entry_to_crate_entry(key: &'cache CStr, value: &'cache CStr) -> Result<Self> {
142 let file_name = utils::os_string_from_cstr(key).map(Cow::Owned)?;
143 let full_path = utils::path_buf_from_cstr(value).map(Cow::Owned)?;
144
145 Ok(Self {
146 file_name,
147 full_path,
148 })
149 }
150}
151
152trait CacheProvider: fmt::Debug + Sync + Send {
153 fn entries_iter<'cache>(
154 &'cache self,
155 ) -> Result<Box<dyn FusedIterator<Item = Result<Entry<'cache>>> + 'cache>>;
156}
157
158#[derive(Debug)]
159enum CacheImpl {
160 LdSO1dot7(ld_so_1dot7::Cache),
161 GLibCLdSOCache1dot1(glibc_ld_so_cache_1dot1::Cache),
162 LdELFSOHints(ld_elf_so_hints::Cache),
163 LdSOHints(ld_so_hints::Cache),
164}
165
166impl AsRef<dyn CacheProvider> for CacheImpl {
167 fn as_ref(&self) -> &(dyn CacheProvider + 'static) {
168 match self {
169 Self::LdSO1dot7(cache) => cache,
170 Self::GLibCLdSOCache1dot1(cache) => cache,
171 Self::LdELFSOHints(cache) => cache,
172 Self::LdSOHints(cache) => cache,
173 }
174 }
175}
176
177#[derive(Debug)]
179pub struct Cache {
180 caches: ArrayVec<CacheImpl, CACHE_IMPL_COUNT>,
181}
182
183impl Cache {
184 pub fn load() -> Result<Self> {
186 const_assert!(size_of::<u32>() <= size_of::<usize>());
187
188 let mut caches = ArrayVec::<CacheImpl, CACHE_IMPL_COUNT>::default();
189
190 if cfg!(target_os = "freebsd") {
191 Self::try_loading_ld_elf_so_hints(&mut caches)?;
192 Self::try_loading_ld_so_hints(&mut caches)?;
193 Self::try_loading_ld_so_1dot7(&mut caches)?;
194 Self::try_loading_glibc_ld_so_cache_1dot1(&mut caches)?;
195 } else if cfg!(any(target_os = "openbsd", target_os = "netbsd")) {
196 Self::try_loading_ld_so_hints(&mut caches)?;
197 Self::try_loading_ld_elf_so_hints(&mut caches)?;
198 Self::try_loading_ld_so_1dot7(&mut caches)?;
199 Self::try_loading_glibc_ld_so_cache_1dot1(&mut caches)?;
200 } else {
201 Self::try_loading_glibc_ld_so_cache_1dot1(&mut caches)?;
202 Self::try_loading_ld_elf_so_hints(&mut caches)?;
203 Self::try_loading_ld_so_hints(&mut caches)?;
204 Self::try_loading_ld_so_1dot7(&mut caches)?;
205 }
206
207 Ok(Self { caches })
208 }
209
210 fn try_loading_glibc_ld_so_cache_1dot1(
211 caches: &mut ArrayVec<CacheImpl, CACHE_IMPL_COUNT>,
212 ) -> Result<()> {
213 if let Ok(cache) = glibc_ld_so_cache_1dot1::Cache::load_default() {
214 caches.push(CacheImpl::GLibCLdSOCache1dot1(cache));
215 }
216 Ok(())
217 }
218
219 fn try_loading_ld_elf_so_hints(
220 caches: &mut ArrayVec<CacheImpl, CACHE_IMPL_COUNT>,
221 ) -> Result<()> {
222 for path in ld_elf_so_hints::CACHE_FILE_PATHS.iter().map(Path::new) {
223 if let Ok(cache) = ld_elf_so_hints::Cache::load(path) {
224 caches.push(CacheImpl::LdELFSOHints(cache));
225 }
226 }
227 Ok(())
228 }
229
230 fn try_loading_ld_so_hints(caches: &mut ArrayVec<CacheImpl, CACHE_IMPL_COUNT>) -> Result<()> {
231 if let Ok(cache) = ld_so_hints::Cache::load_default() {
232 caches.push(CacheImpl::LdSOHints(cache));
233 }
234 Ok(())
235 }
236
237 fn try_loading_ld_so_1dot7(caches: &mut ArrayVec<CacheImpl, CACHE_IMPL_COUNT>) -> Result<()> {
238 if let Ok(cache) = ld_so_1dot7::Cache::load_default() {
239 caches.push(CacheImpl::LdSO1dot7(cache));
240 }
241 Ok(())
242 }
243
244 pub fn iter(&self) -> Result<impl FusedIterator<Item = Result<Entry<'_>>> + '_> {
248 Ok(self
249 .caches
250 .iter()
251 .map(AsRef::as_ref)
252 .map(CacheProvider::entries_iter)
253 .collect::<Result<ArrayVec<_, CACHE_IMPL_COUNT>>>()?
254 .into_iter()
255 .flatten()
256 .fuse())
257 }
258}