monitor/
dlengine.rs

1use std::collections::BTreeMap;
2
3use dynlink::{
4    compartment::{CompartmentId, MONITOR_COMPARTMENT_ID},
5    engines::{Backing, ContextEngine, LoadCtx},
6    library::UnloadedLibrary,
7    DynlinkError, DynlinkErrorKind,
8};
9use smallstr::SmallString;
10use twizzler_abi::{
11    aux::KernelInitInfo,
12    object::{Protections, MAX_SIZE, NULLPAGE_SIZE},
13    syscall::ObjectCreate,
14};
15use twizzler_rt_abi::object::{MapFlags, ObjID};
16use twizzler_security::{SecCtxBase, SecCtxFlags};
17
18use crate::mon::{
19    get_monitor,
20    space::{MapInfo, Space},
21};
22
23pub struct Engine {
24    name_map: BTreeMap<String, ObjID>,
25}
26
27impl Engine {
28    pub fn new() -> Self {
29        let mut name_map = BTreeMap::new();
30        let init_info = get_kernel_init_info();
31        for n in init_info.names() {
32            name_map.insert(n.name().to_string(), n.id());
33        }
34        Self { name_map }
35    }
36
37    pub fn libname_add(&mut self, name: &str, id: ObjID) {
38        self.name_map.insert(name.to_string(), id);
39    }
40
41    pub fn libname_remove(&mut self, name: Option<&str>, id: Option<ObjID>) {
42        if let Some(id) = id {
43            let removed = self
44                .name_map
45                .extract_if(.., |_k, v| *v == id)
46                .collect::<Vec<_>>();
47            for r in removed {
48                tracing::info!("removed {} => {}", r.0, r.1);
49            }
50        }
51        if let Some(name) = name {
52            let r = self.name_map.remove(name);
53            if let Some(r) = r {
54                tracing::info!("removed {} => {}", name, r);
55            }
56        }
57    }
58
59    fn name_resolver(&self, mut name: &str) -> Result<(ObjID, String), DynlinkError> {
60        if name.starts_with("libstd") {
61            name = "libstd.so";
62        }
63        if name.starts_with("libtest") {
64            name = "libtest.so";
65        }
66        if name.contains("libtwz_rt.so") {
67            name = "libtwz_rt.so";
68        }
69
70        if let Some(id) = self.name_map.get(name) {
71            return Ok((*id, name.to_string()));
72        }
73        if name.contains("/") {
74            return self.name_resolver(name.split("/").last().unwrap());
75        }
76        Err(DynlinkError::new(DynlinkErrorKind::NameNotFound {
77            name: SmallString::from_str(name),
78        }))
79    }
80}
81
82fn get_new_sctx_instance(_sctx: ObjID) -> ObjID {
83    let sec_ctx = SecCtxBase::new(Protections::all(), SecCtxFlags::empty());
84
85    let handle = crate::mon::space::Space::safe_create_and_map_object(
86        get_monitor().space,
87        ObjectCreate::default(),
88        &[],
89        &[],
90        MapFlags::READ | MapFlags::WRITE,
91    )
92    .unwrap();
93
94    let base = handle.monitor_data_base().cast::<SecCtxBase>();
95    unsafe { base.write(sec_ctx) };
96    handle.id()
97}
98
99impl ContextEngine for Engine {
100    fn load_segments(
101        &mut self,
102        src: &Backing,
103        ld: &[dynlink::engines::LoadDirective],
104        comp_id: CompartmentId,
105        load_ctx: &mut LoadCtx,
106    ) -> Result<Vec<Backing>, dynlink::DynlinkError> {
107        let instance = *load_ctx
108            .set
109            .entry(comp_id)
110            .or_insert_with(|| get_new_sctx_instance(1.into()));
111        let map = |text_id, data_id| {
112            #[allow(deprecated)]
113            let (text_handle, data_handle) = get_monitor()
114                .space
115                .lock()
116                .unwrap()
117                .map_pair(
118                    MapInfo {
119                        id: text_id,
120                        flags: MapFlags::READ | MapFlags::EXEC,
121                    },
122                    MapInfo {
123                        id: data_id,
124                        flags: MapFlags::READ | MapFlags::WRITE,
125                    },
126                )
127                .map_err(|_| DynlinkErrorKind::NewBackingFail)?;
128
129            if data_handle.monitor_data_start() as usize
130                != text_handle.monitor_data_start() as usize + MAX_SIZE
131            {
132                tracing::error!(
133                                "internal runtime error: failed to map text and data adjacent and in-order ({:p} {:p})",
134                                text_handle.monitor_data_start(),
135                                data_handle.monitor_data_start(),
136                            );
137                return Err(DynlinkErrorKind::NewBackingFail.into());
138            }
139            tracing::trace!(
140                "map {}: {} {}",
141                src.full_name(),
142                text_handle.id(),
143                data_handle.id()
144            );
145            unsafe {
146                Ok((
147                    Backing::new_owned(
148                        text_handle.monitor_data_start(),
149                        MAX_SIZE - NULLPAGE_SIZE * 2,
150                        text_id,
151                        text_handle,
152                        src.full_name().into(),
153                    ),
154                    Backing::new_owned(
155                        data_handle.monitor_data_start(),
156                        MAX_SIZE - NULLPAGE_SIZE * 2,
157                        data_id,
158                        data_handle,
159                        src.full_name().into(),
160                    ),
161                ))
162            }
163        };
164        dynlink::engines::twizzler::load_segments(src, ld, instance, map)
165    }
166
167    fn load_object(&mut self, unlib: &UnloadedLibrary) -> Result<Backing, DynlinkError> {
168        let (id, full) = if unlib.id.is_some() {
169            (unlib.id.unwrap(), unlib.name.clone())
170        } else {
171            self.name_resolver(&unlib.name)
172                .inspect_err(|e| tracing::warn!("failed to find {}: {}", unlib, e))?
173        };
174        let mapping = Space::map(
175            &get_monitor().space,
176            MapInfo {
177                id,
178                flags: MapFlags::READ,
179            },
180        )
181        .map_err(|_err| DynlinkErrorKind::NewBackingFail)
182        .inspect_err(|e| tracing::warn!("failed to map {}: {}", unlib, e))?;
183        Ok(unsafe {
184            Backing::new_owned(
185                mapping.monitor_data_start(),
186                MAX_SIZE - NULLPAGE_SIZE * 2,
187                id,
188                mapping,
189                full,
190            )
191        })
192    }
193
194    fn select_compartment(
195        &mut self,
196        _unlib: &UnloadedLibrary,
197    ) -> Option<dynlink::compartment::CompartmentId> {
198        Some(MONITOR_COMPARTMENT_ID)
199    }
200
201    fn add_name_map(&mut self, name: &str, id: ObjID) {
202        self.libname_add(name, id);
203    }
204
205    fn remove_name_map(&mut self, name: Option<&str>, id: Option<ObjID>) {
206        self.libname_remove(name, id);
207    }
208}
209
210pub fn get_kernel_init_info() -> &'static KernelInitInfo {
211    unsafe {
212        (((twizzler_abi::slot::RESERVED_KERNEL_INIT * MAX_SIZE) + NULLPAGE_SIZE)
213            as *const KernelInitInfo)
214            .as_ref()
215            .unwrap()
216    }
217}