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}