monitor/mon/
library.rs

1use dynlink::{
2    engines::LoadCtx,
3    library::{AllowedGates, LibraryId, UnloadedLibrary},
4    symbol::LookupFlags,
5};
6use happylock::ThreadKey;
7use monitor_api::LibraryInfoRaw;
8use secgate::util::Descriptor;
9use twizzler_abi::object::{MAX_SIZE, NULLPAGE_SIZE};
10use twizzler_rt_abi::{
11    bindings::{ctor_set, link_map},
12    debug::LinkMap,
13    error::{ArgumentError, GenericError, ResourceError, TwzError},
14    object::ObjID,
15};
16
17use super::Monitor;
18
19/// A handle to a library.
20pub struct LibraryHandle {
21    comp: ObjID,
22    id: LibraryId,
23}
24
25impl Monitor {
26    /// Get LibraryInfo for a given library handle. Note that this will write to the
27    /// compartment-thread's simple buffer.
28    pub fn get_library_info(
29        &self,
30        instance: ObjID,
31        thread: ObjID,
32        desc: Descriptor,
33    ) -> Result<LibraryInfoRaw, TwzError> {
34        let (_, ref mut comps, ref dynlink, ref libhandles, _) =
35            *self.locks.lock(ThreadKey::get().unwrap());
36        let handle = libhandles
37            .lookup(instance, desc)
38            .ok_or(ArgumentError::InvalidArgument)?;
39        // TODO: dynlink err map
40        let lib = dynlink
41            .get_library(handle.id)
42            .map_err(|_| GenericError::Internal)?;
43        // write the library name to the per-thread simple buffer
44        let pt = comps.get_mut(instance)?.get_per_thread(thread);
45        let name_len = pt.write_bytes(lib.name.as_bytes());
46        let dynamic_ptr = lib.dynamic_ptr();
47        Ok(LibraryInfoRaw {
48            name_len,
49            compartment_id: handle.comp,
50            objid: lib.full_obj.id(),
51            slot: lib.full_obj.load_addr() / MAX_SIZE,
52            start: (lib.full_obj.load_addr() + NULLPAGE_SIZE) as *mut _,
53            len: MAX_SIZE - NULLPAGE_SIZE * 2,
54            dl_info: twizzler_rt_abi::debug::DlPhdrInfo {
55                addr: lib.base_addr(),
56                name: core::ptr::null(),
57                phdr: lib.get_phdrs_raw().ok_or(GenericError::Internal)?.0 as *const _,
58                phnum: lib.get_phdrs_raw().ok_or(GenericError::Internal)?.1 as u32,
59                adds: 0,
60                subs: 0,
61                tls_modid: lib.tls_id.map(|t| t.tls_id()).unwrap_or(0) as usize,
62                tls_data: core::ptr::null_mut(),
63            },
64            desc,
65            link_map: LinkMap(link_map {
66                next: core::ptr::null_mut(),
67                prev: core::ptr::null_mut(),
68                name: core::ptr::null_mut(),
69                ld: dynamic_ptr.unwrap_or(core::ptr::null_mut()).cast(),
70                addr: lib.base_addr(),
71            }),
72        })
73    }
74
75    /// Open a handle to the n'th library for a compartment.
76    pub fn get_library_handle(
77        &self,
78        caller: ObjID,
79        comp: Option<Descriptor>,
80        num: usize,
81    ) -> Result<Descriptor, TwzError> {
82        let (_, ref mut comps, ref dynlink, ref mut handles, ref comphandles) =
83            *self.locks.lock(ThreadKey::get().unwrap());
84        let comp_id = comp
85            .map(|comp| comphandles.lookup(caller, comp).map(|ch| ch.instance))
86            .unwrap_or(Some(caller))
87            .ok_or(TwzError::INVALID_ARGUMENT)?;
88        let rc = comps.get(comp_id)?;
89        // TODO: dynlink err map
90        let dcomp = dynlink
91            .get_compartment(rc.compartment_id)
92            .map_err(|_| GenericError::Internal)?;
93        let id = dcomp
94            .library_ids()
95            .nth(num)
96            .ok_or(TwzError::INVALID_ARGUMENT)?;
97        handles
98            .insert(caller, LibraryHandle { comp: comp_id, id })
99            .ok_or(ResourceError::OutOfResources.into())
100    }
101
102    /// Load a library by name into the caller's compartment.
103    /// The name is read from the caller's per-thread simple buffer.
104    /// If the library is already loaded, returns a handle to the existing instance.
105    pub fn load_library_by_name(
106        &self,
107        caller: ObjID,
108        thread: ObjID,
109        name_len: usize,
110        id: Option<ObjID>,
111    ) -> Result<(Descriptor, usize), TwzError> {
112        let (_, ref mut comps, ref mut dynlink, ref mut handles, _) =
113            *self.locks.lock(ThreadKey::get().unwrap());
114        let rc = comps.get_mut(caller)?;
115        let name_bytes = rc.get_per_thread(thread).read_bytes(name_len);
116        let name = std::str::from_utf8(&name_bytes)
117            .map_err(|_| ArgumentError::InvalidArgument)?
118            .to_string();
119        let comp_id = rc.compartment_id;
120
121        // If already loaded in this compartment, return a handle to it.
122        let (lib_id, loads) = if let Some(id) = dynlink.lookup_library(comp_id, &name) {
123            (id, None)
124        } else {
125            // Load the library and all its dependencies into the caller's compartment.
126            let unlib = if let Some(id) = id {
127                UnloadedLibrary::new_object(name.clone(), id)
128            } else {
129                UnloadedLibrary::new(name.clone())
130            };
131            let mut load_ctx = LoadCtx::default();
132            let loads = dynlink
133                .load_library_in_compartment(comp_id, unlib, AllowedGates::Private, &mut load_ctx)
134                .map_err(|_| TwzError::NOT_FOUND)?;
135            tracing::debug!("loaded library '{}'", name);
136            let root_id = loads.first().ok_or(GenericError::Internal)?.lib;
137            // Relocate the newly loaded library graph.
138            dynlink
139                .relocate_all(root_id)
140                .map_err(|_| GenericError::Internal)?;
141            (root_id, Some(loads))
142        };
143
144        let ctors = if loads.is_none() {
145            vec![]
146        } else {
147            dynlink
148                .build_ctors_list(lib_id, Some(comp_id), loads)
149                .map_err(|_| TwzError::INVALID_ARGUMENT)?
150        };
151
152        let bytes = unsafe {
153            core::slice::from_raw_parts(
154                ctors.as_ptr().cast::<u8>(),
155                ctors.len() * core::mem::size_of::<ctor_set>(),
156            )
157        };
158        let ctor_len = rc.get_per_thread(thread).write_bytes(bytes);
159
160        handles
161            .insert(
162                caller,
163                LibraryHandle {
164                    comp: caller,
165                    id: lib_id,
166                },
167            )
168            .ok_or(ResourceError::OutOfResources.into())
169            .map(|h| (h, ctor_len))
170    }
171
172    /// Drop a library handle.
173    pub fn drop_library_handle(&self, caller: ObjID, desc: Descriptor) {
174        //tracing::info!("drop: {}", desc);
175        self.library_handles
176            .write(ThreadKey::get().unwrap())
177            .remove(caller, desc);
178    }
179
180    /// Look up a symbol by name in the given library (or all libs in the caller's compartment
181    /// if `lib_desc` is `None`). The symbol name is read from the caller's per-thread simple
182    /// buffer. Returns the relocated symbol address.
183    pub fn lookup_symbol(
184        &self,
185        caller: ObjID,
186        thread: ObjID,
187        lib_desc: Option<Descriptor>,
188        name_len: usize,
189    ) -> Result<usize, TwzError> {
190        let (_, ref mut comps, ref dynlink, ref libhandles, _) =
191            *self.locks.lock(ThreadKey::get().unwrap());
192        let rc = comps.get_mut(caller)?;
193        let name_bytes = rc.get_per_thread(thread).read_bytes(name_len);
194        let name = std::str::from_utf8(&name_bytes).map_err(|_| ArgumentError::InvalidArgument)?;
195
196        tracing::debug!(
197            "looking up symbol '{}' for caller {}, lib_desc = {:?}",
198            name,
199            caller,
200            lib_desc
201        );
202        match lib_desc {
203            Some(desc) => {
204                let lib = libhandles
205                    .lookup(caller, desc)
206                    .ok_or(ArgumentError::InvalidArgument)?;
207                let deps = dynlink.build_deps_search_list(lib.id);
208                let sym = dynlink
209                    .lookup_symbol(
210                        lib.id,
211                        name,
212                        LookupFlags::SKIP_SECGATE_CHECK | LookupFlags::ALLOW_WEAK,
213                        &deps,
214                    )
215                    .map_err(|_| TwzError::NOT_FOUND)?;
216                return Ok(sym.reloc_value() as usize);
217            }
218            None => {
219                // RTLD_DEFAULT: start from the compartment's root (first) library.
220                for lid in dynlink
221                    .get_compartment(rc.compartment_id)
222                    .map_err(|_| GenericError::Internal)?
223                    .library_ids()
224                {
225                    let deps = dynlink.build_deps_search_list(lid);
226                    if let Ok(sym) = dynlink.lookup_symbol(
227                        lid,
228                        name,
229                        LookupFlags::SKIP_SECGATE_CHECK | LookupFlags::ALLOW_WEAK,
230                        &deps,
231                    ) {
232                        return Ok(sym.reloc_value() as usize);
233                    }
234                }
235            }
236        }
237        Err(TwzError::NOT_FOUND)
238    }
239}