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 smallstr::SmallString;
15use twizzler_rt_abi::{
16 core::{CtorSet, RuntimeInfo},
17 debug::LoadedImageId,
18};
19
20use crate::{
21 compartment::CompartmentId, engines::Backing, symbol::RelocatedSymbol, tls::TlsModId,
22 DynlinkError, DynlinkErrorKind,
23};
24
25#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Clone, Copy)]
26pub(crate) enum RelocState {
27 Unrelocated,
29 PartialRelocation,
31 Relocated,
33}
34
35#[derive(PartialEq, PartialOrd, Ord, Eq, Debug, Clone, Copy)]
36pub enum AllowedGates {
37 Private,
39 Public,
41 PublicInclSelf,
43}
44
45#[repr(C)]
46#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Default)]
48pub struct UnloadedLibrary {
49 pub name: String,
50}
51
52impl UnloadedLibrary {
53 pub fn new(name: impl AsRef<str>) -> Self {
55 Self {
56 name: name.as_ref().to_string(),
57 }
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash, Default)]
63#[repr(transparent)]
64pub struct LibraryId(pub(crate) NodeIndex);
65
66impl From<LoadedImageId> for LibraryId {
67 fn from(value: LoadedImageId) -> Self {
68 LibraryId(NodeIndex::new(value as usize))
69 }
70}
71
72impl Into<LoadedImageId> for LibraryId {
73 fn into(self) -> LoadedImageId {
74 self.0.index() as u32
75 }
76}
77
78impl Display for LibraryId {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 write!(f, "{}", &self.0.index())
81 }
82}
83
84#[repr(C)]
85pub struct Library {
87 pub name: String,
89 pub(crate) idx: NodeIndex,
91 pub(crate) comp_id: CompartmentId,
93 comp_name: String,
95 pub full_obj: Backing,
97 pub(crate) reloc_state: RelocState,
99 allowed_gates: AllowedGates,
100
101 pub backings: Vec<Backing>,
102
103 pub tls_id: Option<TlsModId>,
105
106 pub(crate) ctors: CtorSet,
108 pub(crate) secgate_info: SecgateInfo,
109}
110
111#[allow(dead_code)]
112impl Library {
113 pub(crate) fn new(
114 name: String,
115 idx: NodeIndex,
116 comp_id: CompartmentId,
117 comp_name: String,
118 full_obj: Backing,
119 backings: Vec<Backing>,
120 tls_id: Option<TlsModId>,
121 ctors: CtorSet,
122 secgate_info: SecgateInfo,
123 allowed_gates: AllowedGates,
124 ) -> Self {
125 Self {
126 name,
127 idx,
128 full_obj,
129 backings,
130 tls_id,
131 ctors,
132 reloc_state: RelocState::Unrelocated,
133 comp_id,
134 comp_name,
135 secgate_info,
136 allowed_gates,
137 }
138 }
139
140 pub fn allows_gates(&self) -> bool {
141 self.allowed_gates != AllowedGates::Private
142 }
143
144 pub fn allows_self_gates(&self) -> bool {
145 self.allowed_gates == AllowedGates::PublicInclSelf
146 }
147
148 pub fn dynamic_ptr(&self) -> Option<*mut Dyn> {
149 let phdr = self
150 .get_elf()
151 .ok()?
152 .segments()?
153 .iter()
154 .find(|s| s.p_type == PT_DYNAMIC)?;
155 Some(self.laddr_mut(phdr.p_vaddr))
156 }
157
158 pub fn is_binary(&self) -> bool {
159 let Some(dynamic) = self
160 .get_elf()
161 .ok()
162 .and_then(|elf| elf.dynamic().ok())
163 .flatten()
164 else {
165 return false;
166 };
167 let Some(flags) = dynamic.iter().find_map(|ent| {
168 if ent.d_tag == DT_FLAGS_1 {
169 Some(ent.d_val())
170 } else {
171 None
172 }
173 }) else {
174 return false;
175 };
176 flags & elf::abi::DF_1_PIE as u64 != 0
177 }
178
179 pub fn id(&self) -> LibraryId {
181 LibraryId(self.idx)
182 }
183
184 pub fn compartment(&self) -> CompartmentId {
186 self.comp_id
187 }
188
189 pub fn get_phdrs_raw(&self) -> Option<(*const Elf64_Phdr, usize)> {
191 Some((
192 self.get_elf().ok()?.segments()?.iter().find_map(|p| {
193 if p.p_type == PT_PHDR {
194 Some(self.laddr(p.p_vaddr))
195 } else {
196 None
197 }
198 })?,
199 self.get_elf().ok()?.segments()?.len(),
200 ))
201 }
202
203 pub fn get_elf(&self) -> Result<elf::ElfBytes<'_, NativeEndian>, ParseError> {
205 elf::ElfBytes::minimal_parse(self.full_obj.slice())
206 }
207
208 pub fn base_addr(&self) -> usize {
210 self.backings[0].load_addr()
211 }
212
213 pub fn laddr<T>(&self, val: u64) -> *const T {
215 (self.base_addr() + val as usize) as *const T
216 }
217
218 pub fn laddr_mut<T>(&self, val: u64) -> *mut T {
220 (self.base_addr() + val as usize) as *mut T
221 }
222
223 pub fn get_entry_address(
225 &self,
226 ) -> Result<extern "C" fn(*const RuntimeInfo) -> !, DynlinkError> {
227 let entry = self.get_elf()?.ehdr.e_entry;
228 if entry == 0 {
229 return Err(DynlinkErrorKind::NoEntryAddress {
230 name: self.name.as_str().into(),
231 }
232 .into());
233 }
234 let entry: *const u8 = self.laddr(entry);
235 let ptr: extern "C" fn(*const RuntimeInfo) -> ! =
236 unsafe { core::mem::transmute(entry as usize) };
237 Ok(ptr)
238 }
239
240 fn get_tls_phdr(&self) -> Result<Option<ProgramHeader>, DynlinkError> {
242 Ok(self
243 .get_elf()?
244 .segments()
245 .and_then(|phdrs| phdrs.iter().find(|phdr| phdr.p_type == PT_TLS)))
246 }
247
248 pub(crate) fn get_tls_data(&self) -> Result<Option<&[u8]>, DynlinkError> {
249 let phdr = self.get_tls_phdr()?;
250 Ok(phdr.map(|phdr| {
251 let slice = unsafe {
252 core::slice::from_raw_parts(self.laddr(phdr.p_vaddr), phdr.p_memsz as usize)
253 };
254 slice
255 }))
256 }
257
258 fn do_lookup_symbol(
259 &self,
260 name: &str,
261 allow_weak: bool,
262 ) -> Result<RelocatedSymbol<'_>, DynlinkError> {
263 let elf = self.get_elf()?;
264 let common = elf.find_common_data()?;
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".into(),
291 })?,
292 common.dynsyms_strs.as_ref().ok_or_else(|| {
293 DynlinkErrorKind::MissingSection {
294 name: "dynsyms_strs".into(),
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".into(),
327 })?,
328 common.dynsyms_strs.as_ref().ok_or_else(|| {
329 DynlinkErrorKind::MissingSection {
330 name: "dynsyms_strs".into(),
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 { name: name.into() }.into())
380 }
381
382 pub(crate) fn lookup_symbol(
383 &self,
384 name: &str,
385 allow_weak: bool,
386 allow_prefix: bool,
387 ) -> Result<RelocatedSymbol<'_>, DynlinkError> {
388 let ret = self.do_lookup_symbol(&name, allow_weak);
389 if allow_prefix && ret.is_err() && !name.starts_with("__TWIZZLER_SECURE_GATE_") {
390 let mut prefixedname = SmallString::<[u8; 256]>::from_str("__TWIZZLER_SECURE_GATE_");
391 prefixedname.push_str(name);
392
393 if let Ok(o) = self.do_lookup_symbol(&prefixedname, allow_weak) {
394 return Ok(o);
395 }
396 }
397 ret
398 }
399
400 pub(crate) fn is_local_or_secgate_from(&self, other: &Library, name: &str) -> bool {
401 self.in_same_compartment_as(other) || (self.is_relocated() && self.is_secgate(name))
402 }
403
404 pub(crate) fn in_same_compartment_as(&self, other: &Library) -> bool {
405 other.comp_id == self.comp_id
406 }
407
408 pub fn is_relocated(&self) -> bool {
409 self.reloc_state == RelocState::Relocated
410 }
411
412 fn is_secgate(&self, name: &str) -> bool {
413 self.iter_secgates()
414 .map(|gates| {
415 gates
416 .iter()
417 .any(|gate| gate.name().to_bytes() == name.as_bytes())
418 })
419 .unwrap_or(false)
420 }
421
422 pub fn iter_secgates(&self) -> Option<&[RawSecGateInfo]> {
423 let addr = self.secgate_info.info_addr?;
424
425 Some(unsafe { core::slice::from_raw_parts(addr as *const _, self.secgate_info.num) })
426 }
427}
428
429impl Debug for Library {
430 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
431 f.debug_struct("Library")
432 .field("name", &self.name)
433 .field("comp_name", &self.comp_name)
434 .field("idx", &self.idx)
435 .field("tls_id", &self.tls_id)
436 .finish()
437 }
438}
439
440impl Drop for Library {
441 fn drop(&mut self) {
442 }
444}
445
446impl core::fmt::Display for Library {
447 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
448 write!(f, "{}::{}", &self.comp_name, &self.name)
449 }
450}
451
452impl core::fmt::Display for UnloadedLibrary {
453 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
454 write!(f, "{}(unloaded)", &self.name)
455 }
456}
457
458#[derive(Debug, Clone, Default)]
459pub struct SecgateInfo {
460 pub info_addr: Option<usize>,
461 pub num: usize,
462}