1use std::fmt::{Debug, Display};
4
5use elf::{
6 abi::{DT_FLAGS_1, PT_DYNAMIC, PT_PHDR, PT_TLS, STB_WEAK},
7 dynamic::Dyn,
8 endian::NativeEndian,
9 segment::{Elf64_Phdr, ProgramHeader},
10 ParseError,
11};
12use petgraph::stable_graph::NodeIndex;
13use secgate::RawSecGateInfo;
14use twizzler_rt_abi::{
15 core::{CtorSet, RuntimeInfo},
16 debug::LoadedImageId,
17};
18
19use crate::{
20 compartment::CompartmentId, engines::Backing, symbol::RelocatedSymbol, tls::TlsModId,
21 DynlinkError, DynlinkErrorKind,
22};
23
24#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Clone, Copy)]
25pub(crate) enum RelocState {
26 Unrelocated,
28 PartialRelocation,
30 Relocated,
32}
33
34#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Clone, Copy)]
35pub enum AllowedGates {
36 Private,
38 Public,
40 PublicInclSelf,
42}
43
44#[repr(C)]
45#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
47pub struct UnloadedLibrary {
48 pub name: String,
49}
50
51impl UnloadedLibrary {
52 pub fn new(name: impl ToString) -> Self {
54 Self {
55 name: name.to_string(),
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
62#[repr(transparent)]
63pub struct LibraryId(pub(crate) NodeIndex);
64
65impl From<LoadedImageId> for LibraryId {
66 fn from(value: LoadedImageId) -> Self {
67 LibraryId(NodeIndex::new(value as usize))
68 }
69}
70
71impl Into<LoadedImageId> for LibraryId {
72 fn into(self) -> LoadedImageId {
73 self.0.index() as u32
74 }
75}
76
77impl Display for LibraryId {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 write!(f, "{}", &self.0.index())
80 }
81}
82
83#[repr(C)]
84pub struct Library {
86 pub name: String,
88 pub(crate) idx: NodeIndex,
90 pub(crate) comp_id: CompartmentId,
92 comp_name: String,
94 pub full_obj: Backing,
96 pub(crate) reloc_state: RelocState,
98 allowed_gates: AllowedGates,
99
100 pub backings: Vec<Backing>,
101
102 pub tls_id: Option<TlsModId>,
104
105 pub(crate) ctors: CtorSet,
107 pub(crate) secgate_info: SecgateInfo,
108}
109
110#[allow(dead_code)]
111impl Library {
112 pub(crate) fn new(
113 name: String,
114 idx: NodeIndex,
115 comp_id: CompartmentId,
116 comp_name: String,
117 full_obj: Backing,
118 backings: Vec<Backing>,
119 tls_id: Option<TlsModId>,
120 ctors: CtorSet,
121 secgate_info: SecgateInfo,
122 allowed_gates: AllowedGates,
123 ) -> Self {
124 Self {
125 name,
126 idx,
127 full_obj,
128 backings,
129 tls_id,
130 ctors,
131 reloc_state: RelocState::Unrelocated,
132 comp_id,
133 comp_name,
134 secgate_info,
135 allowed_gates,
136 }
137 }
138
139 pub fn allows_gates(&self) -> bool {
140 self.allowed_gates != AllowedGates::Private
141 }
142
143 pub fn allows_self_gates(&self) -> bool {
144 self.allowed_gates == AllowedGates::PublicInclSelf
145 }
146
147 pub fn dynamic_ptr(&self) -> Option<*mut Dyn> {
148 let phdr = self
149 .get_elf()
150 .ok()?
151 .segments()?
152 .iter()
153 .find(|s| s.p_type == PT_DYNAMIC)?;
154 Some(self.laddr_mut(phdr.p_vaddr))
155 }
156
157 pub fn is_binary(&self) -> bool {
158 let Some(dynamic) = self
159 .get_elf()
160 .ok()
161 .and_then(|elf| elf.dynamic().ok())
162 .flatten()
163 else {
164 return false;
165 };
166 let Some(flags) = dynamic.iter().find_map(|ent| {
167 if ent.d_tag == DT_FLAGS_1 {
168 Some(ent.d_val())
169 } else {
170 None
171 }
172 }) else {
173 return false;
174 };
175 flags & elf::abi::DF_1_PIE as u64 != 0
176 }
177
178 pub fn id(&self) -> LibraryId {
180 LibraryId(self.idx)
181 }
182
183 pub fn compartment(&self) -> CompartmentId {
185 self.comp_id
186 }
187
188 pub fn get_phdrs_raw(&self) -> Option<(*const Elf64_Phdr, usize)> {
190 Some((
191 self.get_elf().ok()?.segments()?.iter().find_map(|p| {
192 if p.p_type == PT_PHDR {
193 Some(self.laddr(p.p_vaddr))
194 } else {
195 None
196 }
197 })?,
198 self.get_elf().ok()?.segments()?.len(),
199 ))
200 }
201
202 pub fn get_elf(&self) -> Result<elf::ElfBytes<'_, NativeEndian>, ParseError> {
204 elf::ElfBytes::minimal_parse(self.full_obj.slice())
205 }
206
207 pub fn base_addr(&self) -> usize {
209 self.backings[0].load_addr()
210 }
211
212 pub fn laddr<T>(&self, val: u64) -> *const T {
214 (self.base_addr() + val as usize) as *const T
215 }
216
217 pub fn laddr_mut<T>(&self, val: u64) -> *mut T {
219 (self.base_addr() + val as usize) as *mut T
220 }
221
222 pub fn get_entry_address(
224 &self,
225 ) -> Result<extern "C" fn(*const RuntimeInfo) -> !, DynlinkError> {
226 let entry = self.get_elf()?.ehdr.e_entry;
227 if entry == 0 {
228 return Err(DynlinkErrorKind::NoEntryAddress {
229 name: self.name.clone(),
230 }
231 .into());
232 }
233 let entry: *const u8 = self.laddr(entry);
234 let ptr: extern "C" fn(*const RuntimeInfo) -> ! =
235 unsafe { core::mem::transmute(entry as usize) };
236 Ok(ptr)
237 }
238
239 fn get_tls_phdr(&self) -> Result<Option<ProgramHeader>, DynlinkError> {
241 Ok(self
242 .get_elf()?
243 .segments()
244 .and_then(|phdrs| phdrs.iter().find(|phdr| phdr.p_type == PT_TLS)))
245 }
246
247 pub(crate) fn get_tls_data(&self) -> Result<Option<&[u8]>, DynlinkError> {
248 let phdr = self.get_tls_phdr()?;
249 Ok(phdr.map(|phdr| {
250 let slice = unsafe {
251 core::slice::from_raw_parts(self.laddr(phdr.p_vaddr), phdr.p_memsz as usize)
252 };
253 slice
254 }))
255 }
256
257 fn do_lookup_symbol(
258 &self,
259 name: &str,
260 allow_weak: bool,
261 ) -> Result<RelocatedSymbol<'_>, DynlinkError> {
262 let elf = self.get_elf()?;
263 let common = elf.find_common_data()?;
264 tracing::debug!("lookup {} in {}", name, self.name);
265
266 if let Some(h) = &common.gnu_hash {
283 if let Some((_, sym)) = h
284 .find(
285 name.as_ref(),
286 common
287 .dynsyms
288 .as_ref()
289 .ok_or_else(|| DynlinkErrorKind::MissingSection {
290 name: "dynsyms".to_string(),
291 })?,
292 common.dynsyms_strs.as_ref().ok_or_else(|| {
293 DynlinkErrorKind::MissingSection {
294 name: "dynsyms_strs".to_string(),
295 }
296 })?,
297 )
298 .ok()
299 .flatten()
300 {
301 if !sym.is_undefined() {
302 if sym.st_bind() != STB_WEAK
304 || allow_weak
305 || (self.is_relocated() && self.is_secgate(name))
306 {
307 return Ok(RelocatedSymbol::new(sym, self));
308 } else {
309 tracing::warn!("lookup symbol {} skipping weak binding in {}", name, self);
310 }
311 } else {
312 }
314 }
315 }
316
317 if let Some(h) = &common.sysv_hash {
319 if let Some((_, sym)) = h
320 .find(
321 name.as_ref(),
322 common
323 .dynsyms
324 .as_ref()
325 .ok_or_else(|| DynlinkErrorKind::MissingSection {
326 name: "dynsyms".to_string(),
327 })?,
328 common.dynsyms_strs.as_ref().ok_or_else(|| {
329 DynlinkErrorKind::MissingSection {
330 name: "dynsyms_strs".to_string(),
331 }
332 })?,
333 )
334 .ok()
335 .flatten()
336 {
337 if !sym.is_undefined() {
338 if sym.st_bind() != STB_WEAK
340 || allow_weak
341 || (self.is_relocated() && self.is_secgate(name))
342 {
343 return Ok(RelocatedSymbol::new(sym, self));
344 } else {
345 tracing::warn!("lookup symbol {} skipping weak binding in {}", name, self);
346 }
347 } else {
348 }
350 }
351 }
352
353 if !self.allows_gates()
354 && !self.allows_self_gates()
355 && self.is_binary()
356 && !name.starts_with("__TWIZZLER_SECURE_GATE")
357 {
358 let dstrs = common.dynsyms_strs.as_ref().unwrap();
359 for sym in common.dynsyms.as_ref().unwrap().iter() {
360 let sym_name = dstrs.get(sym.st_name as usize)?;
361 if name == sym_name {
362 if sym.st_bind() == STB_WEAK && allow_weak && !self.is_secgate(name) {
363 return Ok(RelocatedSymbol::new_zero(self));
371 } else {
372 }
375 }
376 }
377 }
378 Err(DynlinkErrorKind::NameNotFound {
380 name: name.to_string(),
381 }
382 .into())
383 }
384
385 pub(crate) fn lookup_symbol(
386 &self,
387 name: &str,
388 allow_weak: bool,
389 allow_prefix: bool,
390 ) -> Result<RelocatedSymbol<'_>, DynlinkError> {
391 let ret = self.do_lookup_symbol(&name, allow_weak);
392 if allow_prefix && ret.is_err() && !name.starts_with("__TWIZZLER_SECURE_GATE_") {
393 let name = format!("__TWIZZLER_SECURE_GATE_{}", name);
394 if let Ok(o) = self.do_lookup_symbol(&name, allow_weak) {
395 return Ok(o);
396 }
397 }
398 ret
399 }
400
401 pub(crate) fn is_local_or_secgate_from(&self, other: &Library, name: &str) -> bool {
402 self.in_same_compartment_as(other) || (self.is_relocated() && self.is_secgate(name))
403 }
404
405 pub(crate) fn in_same_compartment_as(&self, other: &Library) -> bool {
406 other.comp_id == self.comp_id
407 }
408
409 pub fn is_relocated(&self) -> bool {
410 self.reloc_state == RelocState::Relocated
411 }
412
413 fn is_secgate(&self, name: &str) -> bool {
414 self.iter_secgates()
415 .map(|gates| {
416 gates
417 .iter()
418 .any(|gate| gate.name().to_bytes() == name.as_bytes())
419 })
420 .unwrap_or(false)
421 }
422
423 pub fn iter_secgates(&self) -> Option<&[RawSecGateInfo]> {
424 let addr = self.secgate_info.info_addr?;
425
426 Some(unsafe { core::slice::from_raw_parts(addr as *const _, self.secgate_info.num) })
427 }
428}
429
430impl Debug for Library {
431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432 f.debug_struct("Library")
433 .field("name", &self.name)
434 .field("comp_name", &self.comp_name)
435 .field("idx", &self.idx)
436 .field("tls_id", &self.tls_id)
437 .finish()
438 }
439}
440
441impl Drop for Library {
442 fn drop(&mut self) {
443 }
445}
446
447impl core::fmt::Display for Library {
448 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449 write!(f, "{}::{}", &self.comp_name, &self.name)
450 }
451}
452
453impl core::fmt::Display for UnloadedLibrary {
454 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
455 write!(f, "{}(unloaded)", &self.name)
456 }
457}
458
459#[derive(Debug, Clone, Default)]
460pub struct SecgateInfo {
461 pub info_addr: Option<usize>,
462 pub num: usize,
463}