1use elf::{endian::NativeEndian, string_table::StringTable, symbol::SymbolTable};
2use tracing::error;
3
4use crate::{
5 context::{relocate::EitherRel, Context},
6 library::Library,
7 symbol::LookupFlags,
8 tls::{TlsRegion, TlsVariant},
9 DynlinkError, DynlinkErrorKind,
10};
11
12pub(crate) const MINIMUM_TLS_ALIGNMENT: usize = 32;
13
14pub use elf::abi::{
15 R_X86_64_64 as REL_SYMBOLIC, R_X86_64_DTPMOD64 as REL_DTPMOD, R_X86_64_DTPOFF64 as REL_DTPOFF,
16 R_X86_64_GLOB_DAT as REL_GOT, R_X86_64_JUMP_SLOT as REL_PLT, R_X86_64_RELATIVE as REL_RELATIVE,
17 R_X86_64_TPOFF64 as REL_TPOFF, STB_WEAK,
18};
19
20#[repr(C)]
21pub struct Tcb<T> {
22 pub self_ptr: *const Tcb<T>,
23 pub dtv_len: usize,
24 pub dtv: *const usize,
25 pub runtime_data: T,
26}
27
28pub fn get_tls_variant() -> TlsVariant {
30 TlsVariant::Variant2
31}
32
33pub unsafe fn get_current_thread_control_block<T>() -> *mut Tcb<T> {
38 let mut val: usize;
39 core::arch::asm!("mov {}, fs:0", out(reg) val);
40 val as *mut _
41}
42
43impl TlsRegion {
44 pub unsafe fn get_thread_control_block<T>(&self) -> *mut Tcb<T> {
49 self.get_thread_pointer_value() as *mut _
50 }
51}
52
53impl Context {
54 pub(crate) fn do_reloc(
55 &self,
56 lib: &Library,
57 rel: EitherRel,
58 strings: &StringTable,
59 syms: &SymbolTable<NativeEndian>,
60 ) -> Result<(), DynlinkError> {
61 let addend = rel.addend();
62 let base = lib.base_addr() as u64;
63 let target: *mut u64 = lib.laddr_mut(rel.offset());
64 let mut is_weak = false;
65 let symbol = if rel.sym() != 0 {
67 let sym = syms.get(rel.sym() as usize)?;
68 let flags = if sym.st_bind() == STB_WEAK {
69 is_weak = true;
70 LookupFlags::ALLOW_WEAK
71 } else {
72 LookupFlags::ALLOW_WEAK
73 };
74 strings
75 .get(sym.st_name as usize)
76 .map(|name| (name, self.lookup_symbol(lib.id(), name, flags)))
77 .ok()
78 } else {
79 None
80 };
81 let sn = symbol.as_ref().map(|s| s.0.to_string()).unwrap_or_default();
82
83 let open_sym = || {
85 if let Some((name, sym)) = symbol {
86 if let Ok(sym) = sym {
87 Result::<_, DynlinkError>::Ok(sym)
88 } else if is_weak {
89 Result::<_, DynlinkError>::Ok(crate::symbol::RelocatedSymbol::new_zero(lib))
90 } else {
91 error!("{}: needed symbol {} not found", lib, name);
92 Err(DynlinkErrorKind::SymbolLookupFail {
93 symname: name.to_string(),
94 sourcelib: lib.name.to_string(),
95 }
96 .into())
97 }
98 } else {
99 error!("{}: invalid relocation, no symbol data", lib);
100 Err(DynlinkErrorKind::MissingSection {
101 name: "symbol data".to_string(),
102 }
103 .into())
104 }
105 };
106
107 match rel.r_type() {
109 REL_RELATIVE => unsafe { *target = base.wrapping_add_signed(addend) },
110 REL_SYMBOLIC => unsafe {
111 *target = open_sym()?.reloc_value().wrapping_add_signed(addend)
112 },
113 REL_PLT | REL_GOT => unsafe { *target = open_sym()?.reloc_value() },
114 REL_DTPMOD => {
115 let id = if rel.sym() == 0 {
117 lib.tls_id
118 .as_ref()
119 .ok_or_else(|| DynlinkErrorKind::NoTLSInfo {
120 library: lib.name.clone(),
121 })?
122 .tls_id()
123 } else {
124 let other_lib = open_sym()?.lib;
125 other_lib
126 .tls_id
127 .as_ref()
128 .ok_or_else(|| DynlinkErrorKind::NoTLSInfo {
129 library: other_lib.name.clone(),
130 })?
131 .tls_id()
132 };
133 unsafe { *target = id }
134 }
135 REL_DTPOFF => {
136 let val = open_sym().map(|sym| sym.raw_value()).unwrap_or(0);
137 unsafe { *target = val.wrapping_add_signed(addend) }
138 }
139 REL_TPOFF => {
140 let sym = open_sym()?;
141 if let Some(tls) = sym.lib.tls_id {
142 unsafe {
143 *target = sym
144 .raw_value()
145 .wrapping_sub(tls.offset() as u64)
146 .wrapping_add_signed(addend)
147 }
148 } else {
149 error!(
150 "{}: TPOFF relocations require a PT_TLS segment (sym {})",
151 lib, sn
152 );
153 Err(DynlinkErrorKind::NoTLSInfo {
154 library: lib.name.clone(),
155 })?
156 }
157 }
158 _ => {
159 error!("{}: unsupported relocation: {}", lib, rel.r_type());
160 Result::<_, DynlinkError>::Err(
161 DynlinkErrorKind::UnsupportedReloc {
162 library: lib.name.clone(),
163 reloc: rel.r_type().to_string(),
164 }
165 .into(),
166 )?
167 }
168 }
169
170 Ok(())
171 }
172}