1use std::mem::size_of;
2
3use elf::{
4 abi::{
5 DT_INIT, DT_INIT_ARRAY, DT_INIT_ARRAYSZ, DT_PREINIT_ARRAY, DT_PREINIT_ARRAYSZ, PT_DYNAMIC,
6 PT_TLS,
7 },
8 dynamic::DynamicTable,
9 endian::NativeEndian,
10 file::Class,
11};
12use petgraph::stable_graph::NodeIndex;
13use secgate::RawSecGateInfo;
14use tracing::{debug, warn};
15use twizzler_rt_abi::{core::CtorSet, object::MAX_SIZE};
16
17use super::{Context, LoadedOrUnloaded};
18use crate::{
19 compartment::{Compartment, CompartmentId},
20 context::NewCompartmentFlags,
21 engines::{LoadCtx, LoadDirective, LoadFlags},
22 library::{AllowedGates, Library, LibraryId, SecgateInfo, UnloadedLibrary},
23 tls::TlsModule,
24 DynlinkError, DynlinkErrorKind, HeaderError, Vec, SMALL_VEC_SIZE,
25};
26
27#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash, Default)]
28pub struct LoadIds {
29 pub comp: CompartmentId,
30 pub lib: LibraryId,
31}
32
33impl From<&Library> for LoadIds {
34 fn from(value: &Library) -> Self {
35 Self {
36 comp: value.comp_id,
37 lib: value.id(),
38 }
39 }
40}
41
42impl Context {
43 pub(crate) fn get_secgate_info(
44 &self,
45 libname: &str,
46 elf: &elf::ElfBytes<'_, NativeEndian>,
47 base_addr: usize,
48 ) -> Result<SecgateInfo, DynlinkError> {
49 let info = elf
50 .section_header_by_name(".twz_secgate_info")?
51 .map(|info| SecgateInfo {
52 info_addr: Some((info.sh_addr as usize) + base_addr),
53 num: (info.sh_size as usize) / core::mem::size_of::<RawSecGateInfo>(),
54 })
55 .unwrap_or_default();
56
57 debug!(
58 "{}: registered secure gate info: {} gates",
59 libname, info.num
60 );
61
62 Ok(info)
63 }
64
65 pub(crate) fn get_ctor_info(
67 &self,
68 libname: &str,
69 elf: &elf::ElfBytes<'_, NativeEndian>,
70 base_addr: usize,
71 ) -> Result<CtorSet, DynlinkError> {
72 let ph = elf.segments();
73
74 let ph = ph.unwrap();
75 let mut dynamic = None;
76 for p in ph {
77 if p.p_type == PT_DYNAMIC {
78 let slice = unsafe {
79 std::slice::from_raw_parts(
80 (p.p_vaddr + base_addr as u64) as *const u8,
81 p.p_memsz as usize,
82 )
83 };
84 dynamic = Some(DynamicTable::new(NativeEndian, Class::ELF64, slice));
85 }
86 }
87 let dynamic = dynamic.ok_or_else(|| {
88 DynlinkError::new(DynlinkErrorKind::MissingSection {
89 name: "dynamic".into(),
90 })
91 })?;
92
93 let init_array_len = dynamic
96 .iter()
97 .find_map(|d| {
98 if d.d_tag == DT_INIT_ARRAYSZ {
99 Some((d.d_val() as usize) / size_of::<usize>())
100 } else {
101 None
102 }
103 })
104 .unwrap_or_default();
105
106 let init_array = dynamic.iter().find_map(|d| {
108 if d.d_tag == DT_INIT_ARRAY && d.clone().d_ptr() != 0 {
109 Some(base_addr + d.d_ptr() as usize)
110 } else {
111 None
112 }
113 });
114
115 let leg_init = dynamic.iter().find_map(|d| {
117 if d.d_tag == DT_INIT && d.clone().d_ptr() != 0 {
118 Some(base_addr + d.d_ptr() as usize)
119 } else {
120 None
121 }
122 });
123
124 if dynamic.iter().any(|d| d.d_tag == DT_PREINIT_ARRAY)
125 && dynamic
126 .iter()
127 .find(|d| d.d_tag == DT_PREINIT_ARRAYSZ)
128 .is_some_and(|d| d.d_val() > 0)
129 {
130 warn!("{}: PREINIT_ARRAY is unsupported", libname);
131 }
132
133 tracing::debug!(
134 "{}: ctor info: init_array: {:?} len={}, legacy: {:?}",
135 libname,
136 init_array,
137 init_array_len,
138 leg_init
139 );
140 Ok(CtorSet {
141 legacy_init: leg_init.map(|x| unsafe { std::mem::transmute(x) }),
142 init_array: init_array.unwrap_or_default() as *mut _,
143 init_array_len,
144 })
145 }
146
147 fn load(
150 &mut self,
151 comp_id: CompartmentId,
152 unlib: UnloadedLibrary,
153 idx: NodeIndex,
154 allowed_gates: AllowedGates,
155 load_ctx: &mut LoadCtx,
156 ) -> Result<Library, DynlinkError> {
157 tracing::debug!(
158 "loading library {} (idx = {:?}) into comp {}",
159 unlib,
160 idx,
161 comp_id
162 );
163 let backing = self.engine.load_object(&unlib)?;
164 let elf = backing.get_elf()?;
165
166 const EXPECTED_CLASS: Class = Class::ELF64;
169 const EXPECTED_VERSION: u32 = 1;
170 const EXPECTED_ABI: u8 = elf::abi::ELFOSABI_SYSV;
171 const EXPECTED_ABI_VERSION: u8 = 0;
172 const EXPECTED_TYPE: u16 = elf::abi::ET_DYN;
173
174 #[cfg(target_arch = "x86_64")]
175 const EXPECTED_MACHINE: u16 = elf::abi::EM_X86_64;
176
177 #[cfg(target_arch = "aarch64")]
178 const EXPECTED_MACHINE: u16 = elf::abi::EM_AARCH64;
179
180 if elf.ehdr.class != EXPECTED_CLASS {
181 return Err(DynlinkErrorKind::from(HeaderError::ClassMismatch {
182 expect: Class::ELF64,
183 got: elf.ehdr.class,
184 })
185 .into());
186 }
187
188 if elf.ehdr.version != EXPECTED_VERSION {
189 return Err(DynlinkErrorKind::from(HeaderError::VersionMismatch {
190 expect: EXPECTED_VERSION,
191 got: elf.ehdr.version,
192 })
193 .into());
194 }
195
196 if elf.ehdr.osabi != EXPECTED_ABI {
197 return Err(DynlinkErrorKind::from(HeaderError::OSABIMismatch {
198 expect: EXPECTED_ABI,
199 got: elf.ehdr.osabi,
200 })
201 .into());
202 }
203
204 if elf.ehdr.abiversion != EXPECTED_ABI_VERSION {
205 return Err(DynlinkErrorKind::from(HeaderError::ABIVersionMismatch {
206 expect: EXPECTED_ABI_VERSION,
207 got: elf.ehdr.abiversion,
208 })
209 .into());
210 }
211
212 if elf.ehdr.e_machine != EXPECTED_MACHINE {
213 return Err(DynlinkErrorKind::from(HeaderError::MachineMismatch {
214 expect: EXPECTED_MACHINE,
215 got: elf.ehdr.e_machine,
216 })
217 .into());
218 }
219
220 if elf.ehdr.e_type != EXPECTED_TYPE {
221 return Err(DynlinkErrorKind::from(HeaderError::ELFTypeMismatch {
222 expect: EXPECTED_TYPE,
223 got: elf.ehdr.e_type,
224 })
225 .into());
226 }
227
228 let directives: Vec<_, SMALL_VEC_SIZE> = elf
231 .segments()
232 .ok_or_else(|| DynlinkErrorKind::MissingSection {
233 name: "segment info".into(),
234 })?
235 .iter()
236 .filter(|p| p.p_type == elf::abi::PT_LOAD)
237 .map(|phdr| {
238 let ld = LoadDirective {
239 load_flags: if phdr.p_flags & elf::abi::PF_W != 0
240 || phdr.p_vaddr > MAX_SIZE as u64
241 {
242 LoadFlags::TARGETS_DATA
243 } else {
244 LoadFlags::empty()
245 },
246 vaddr: phdr.p_vaddr as usize,
247 memsz: phdr.p_memsz as usize,
248 offset: phdr.p_offset as usize,
249 align: phdr.p_align as usize,
250 filesz: phdr.p_filesz as usize,
251 };
252
253 debug!("{}: {:?}", unlib, ld);
254
255 ld
256 })
257 .collect();
258
259 let backings = self
261 .engine
262 .load_segments(&backing, &directives, comp_id, load_ctx)?;
263
264 if backings.is_empty() {
265 return Err(DynlinkErrorKind::NewBackingFail.into());
266 }
267 let base_addr = backings[0].load_addr();
268
269 debug!(
270 "{}: loaded to {:x} (data at {:x})",
271 unlib,
272 base_addr,
273 backings.get(1).map(|b| b.load_addr()).unwrap_or_default()
274 );
275
276 let tls_phdr = elf
278 .segments()
279 .and_then(|phdrs| phdrs.iter().find(|phdr| phdr.p_type == PT_TLS));
280
281 let tls_id = tls_phdr
282 .map(|tls_phdr| {
283 let formatter = humansize::make_format(humansize::BINARY);
284 debug!(
285 "{}: registering TLS data ({} total, {} copy)",
286 unlib,
287 formatter(tls_phdr.p_memsz),
288 formatter(tls_phdr.p_filesz)
289 );
290 let tm = TlsModule::new_static(
291 base_addr + tls_phdr.p_vaddr as usize,
292 tls_phdr.p_filesz as usize,
293 tls_phdr.p_memsz as usize,
294 tls_phdr.p_align as usize,
295 );
296 let comp = &mut self.get_compartment_mut(comp_id)?;
297 Ok::<_, DynlinkError>(comp.insert(tm))
298 })
299 .transpose()?;
300
301 debug!("{}: got TLS ID {:?}", unlib, tls_id);
302
303 let ctor_info = self.get_ctor_info(&unlib.name, &elf, base_addr)?;
305 let secgate_info = self.get_secgate_info(&unlib.name, &elf, base_addr)?;
306
307 let comp = self.get_compartment(comp_id)?;
308 Ok(Library::new(
309 backing.full_name().to_owned(),
310 idx,
311 comp.id,
312 comp.name.clone(),
313 backing,
314 backings,
315 tls_id,
316 ctor_info,
317 secgate_info,
318 allowed_gates,
319 ))
320 }
321
322 fn find_cross_compartment_library(
323 &self,
324 unlib: &UnloadedLibrary,
325 ) -> Option<(NodeIndex, CompartmentId, &Compartment)> {
326 for (idx, comp) in self.compartments.iter().enumerate() {
327 if let Some(lib_id) = comp.1.library_names.get(&unlib.name) {
328 let lib = self.get_library(LibraryId(*lib_id));
329 if let Ok(lib) = lib {
330 if lib.secgate_info.info_addr.is_some() && lib.allows_gates() {
332 return Some((*lib_id, CompartmentId(idx), comp.1));
333 }
334 return None;
335 }
336 }
337 }
338
339 None
340 }
341
342 fn has_secgate_info(&self, elf: &elf::ElfBytes<'_, NativeEndian>) -> bool {
343 elf.section_header_by_name(".twz_secgate_info")
344 .ok()
345 .is_some_and(|s| s.is_some())
346 }
347
348 fn select_compartment(
349 &mut self,
350 unlib: &UnloadedLibrary,
351 parent_comp_name: String,
352 ) -> Option<CompartmentId> {
353 let backing = self.engine.load_object(unlib).ok()?;
354 let elf = backing.get_elf().ok()?;
355 if self.has_secgate_info(&elf) {
356 let name = format!("{}::{}", parent_comp_name, unlib.name);
357 let id = self
358 .add_compartment(&name, NewCompartmentFlags::empty())
359 .ok()?;
360 tracing::debug!(
361 "creating new compartment {}({}) for library {}",
362 name,
363 id,
364 unlib.name
365 );
366 Some(id)
368 } else {
369 None
370 }
371 }
372
373 pub(crate) fn load_library(
375 &mut self,
376 comp_id: CompartmentId,
377 root_unlib: UnloadedLibrary,
378 idx: NodeIndex,
379 allowed_gates: AllowedGates,
380 load_ctx: &mut LoadCtx,
381 ) -> Result<Vec<LoadIds, SMALL_VEC_SIZE>, DynlinkError> {
382 let root_comp_name = self.get_compartment(comp_id)?.name.clone();
383 tracing::debug!(
384 "loading library {} (idx = {:?}) in {}",
385 root_unlib,
386 idx,
387 root_comp_name
388 );
389 let mut ids = Vec::new();
390 let lib = self
392 .load(comp_id, root_unlib.clone(), idx, allowed_gates, load_ctx)
393 .map_err(|e| {
394 DynlinkError::new_collect(
395 DynlinkErrorKind::LibraryLoadFail {
396 library: root_unlib.clone(),
397 },
398 vec![e],
399 )
400 })?;
401 ids.push((&lib).into());
402
403 tracing::debug!("enumerating deps for {}", lib);
404 let deps = self.enumerate_needed(&lib).map_err(|e| {
406 DynlinkError::new_collect(
407 DynlinkErrorKind::DepEnumerationFail {
408 library: root_unlib.name.as_str().into(),
409 },
410 vec![e],
411 )
412 })?;
413 if !deps.is_empty() {
414 debug!("{}: loading {} dependencies", root_unlib, deps.len());
415 }
416 let deps = deps
417 .iter()
418 .map(|dep_unlib| {
419 let comp = self.get_compartment(comp_id)?;
429 let (existing_idx, load_comp) =
430 if let Some(existing) = comp.library_names.get(&dep_unlib.name) {
431 debug!(
432 "{}: dep using existing library for {} (intra-compartment in {}): {:?}",
433 root_unlib, dep_unlib.name, comp.name, existing
434 );
435 (Some(*existing), comp_id)
436 } else if let Some((existing, other_comp_id, other_comp)) =
437 self.find_cross_compartment_library(&dep_unlib)
438 {
439 debug!(
440 "{}: dep using existing library for {} (cross-compartment to {}): {:?}",
441 root_unlib, dep_unlib.name, other_comp.name, existing
442 );
443 (Some(existing), other_comp_id)
444 } else {
445 (
446 None,
447 self.select_compartment(&dep_unlib, root_comp_name.clone())
448 .unwrap_or(comp_id),
449 )
450 };
451
452 let idx = if let Some(existing_idx) = existing_idx {
455 existing_idx
456 } else {
457 let idx = self.add_library(dep_unlib.clone());
458
459 let comp = self.get_compartment_mut(load_comp)?;
460 comp.library_names.insert(dep_unlib.name.clone(), idx);
461 let allowed_gates = if comp.id == comp_id {
462 AllowedGates::Private
463 } else {
464 AllowedGates::Public
465 };
466 let recs = self
467 .load_library(load_comp, dep_unlib.clone(), idx, allowed_gates, load_ctx)
468 .map_err(|e| {
469 tracing::error!("failed to load dependency for {}: {}", lib, e);
470 DynlinkError::new_collect(
471 DynlinkErrorKind::LibraryLoadFail {
472 library: dep_unlib.clone(),
473 },
474 vec![e],
475 )
476 })?;
477 ids.extend_from_slice(recs.as_slice());
478 idx
479 };
480 self.add_dep(lib.idx, idx);
481 Ok(idx)
482 })
483 .collect::<std::vec::Vec<Result<_, DynlinkError>>>();
484
485 let _ = DynlinkError::collect(
486 DynlinkErrorKind::LibraryLoadFail {
487 library: root_unlib,
488 },
489 deps,
490 )?;
491
492 assert_eq!(idx, lib.idx);
493 self.library_deps[idx] = LoadedOrUnloaded::Loaded(lib);
494 Ok(ids)
495 }
496
497 pub fn load_library_in_compartment(
499 &mut self,
500 comp_id: CompartmentId,
501 unlib: UnloadedLibrary,
502 allowed_gates: AllowedGates,
503 load_ctx: &mut LoadCtx,
504 ) -> Result<Vec<LoadIds, SMALL_VEC_SIZE>, DynlinkError> {
505 let idx = self.add_library(unlib.clone());
506 let comp = self.get_compartment_mut(comp_id)?;
508
509 if comp.library_names.contains_key(&unlib.name) {
511 return Err(DynlinkErrorKind::NameAlreadyExists {
512 name: unlib.name.as_str().into(),
513 }
514 .into());
515 }
516 comp.library_names.insert(unlib.name.clone(), idx);
517
518 self.load_library(comp_id, unlib.clone(), idx, allowed_gates, load_ctx)
520 }
521}