1use elf::{endian::NativeEndian, string_table::StringTable, symbol::SymbolTable};
2use petgraph::graph::NodeIndex;
3use tracing::error;
4
5use crate::{
6 context::{
7 relocate::{EitherRel, RelocCache},
8 Context,
9 },
10 library::Library,
11 symbol::LookupFlags,
12 tls::{TlsRegion, TlsVariant},
13 DynlinkError, DynlinkErrorKind,
14};
15
16pub(crate) const MINIMUM_TLS_ALIGNMENT: usize = 32;
17
18pub use elf::abi::{
19 R_X86_64_64 as REL_SYMBOLIC, R_X86_64_DTPMOD64 as REL_DTPMOD, R_X86_64_DTPOFF64 as REL_DTPOFF,
20 R_X86_64_GLOB_DAT as REL_GOT, R_X86_64_JUMP_SLOT as REL_PLT, R_X86_64_RELATIVE as REL_RELATIVE,
21 R_X86_64_TPOFF64 as REL_TPOFF, STB_WEAK,
22};
23
24#[repr(C)]
25pub struct Tcb<T> {
26 pub self_ptr: *const Tcb<T>,
27 pub dtv_len: usize,
28 pub dtv: *const usize,
29 pub runtime_data: T,
30}
31
32pub fn get_tls_variant() -> TlsVariant {
34 TlsVariant::Variant2
35}
36
37pub unsafe fn get_current_thread_control_block<T>() -> *mut Tcb<T> {
42 let mut val: usize;
43 core::arch::asm!("mov {}, fs:0", out(reg) val);
44 val as *mut _
45}
46
47impl TlsRegion {
48 pub unsafe fn get_thread_control_block<T>(&self) -> *mut Tcb<T> {
53 self.get_thread_pointer_value() as *mut _
54 }
55}
56
57impl Context {
58 pub(crate) fn do_reloc(
59 &self,
60 lib: &Library,
61 rel: EitherRel,
62 strings: &StringTable,
63 syms: &SymbolTable<NativeEndian>,
64 deps_list: &[NodeIndex],
65 reloc_cache: &mut RelocCache<'_>,
66 ) -> Result<(), DynlinkError> {
67 let base = lib.base_addr() as u64;
68 let target: *mut u64 = lib.laddr_mut(rel.offset());
69 let addend = rel.addend(target);
70 let mut is_weak = false;
71 let symbol = if rel.sym() != 0 {
73 let sym = syms.get(rel.sym() as usize)?;
74 let flags = if sym.st_bind() == STB_WEAK {
75 is_weak = true;
76 LookupFlags::ALLOW_WEAK
77 } else {
78 LookupFlags::ALLOW_WEAK
79 };
80 let _start = std::time::Instant::now();
81 let r = strings
82 .get(sym.st_name as usize)
83 .map(|name| {
84 let sym = match reloc_cache.find(name, lib.comp_id) {
85 Some(sym) => {
86 tracing::trace!("found {} in cache", name);
87 Ok(sym.clone())
88 }
89 None => {
90 let sym = self.lookup_symbol(lib.id(), name, flags, deps_list);
91 if let Ok(ref sym) = sym {
92 reloc_cache.insert(name, lib.comp_id, unsafe {
93 std::mem::transmute(sym.clone())
94 });
95 }
96 sym
97 }
98 };
99
100 (name, sym)
101 })
102 .ok();
103 tracing::trace!(
104 "lookup {:?} cost {}us",
105 r.as_ref().map(|r| r.0),
106 _start.elapsed().as_micros()
107 );
108 r
109 } else {
110 None
111 };
112 let sn = symbol.as_ref().map(|s| s.0).unwrap_or_default();
113
114 let open_sym = || {
116 if let Some((name, sym)) = symbol {
117 if let Ok(sym) = sym {
118 Result::<_, DynlinkError>::Ok(sym)
119 } else if is_weak {
120 Result::<_, DynlinkError>::Ok(crate::symbol::RelocatedSymbol::new_zero(lib))
121 } else {
122 error!("{}: needed symbol {} not found", lib, name);
123 Err(DynlinkErrorKind::SymbolLookupFail {
124 symname: name.into(),
125 sourcelib: lib.name.as_str().into(),
126 }
127 .into())
128 }
129 } else {
130 error!("{}: invalid relocation, no symbol data", lib);
131 Err(DynlinkErrorKind::MissingSection {
132 name: "symbol data".into(),
133 }
134 .into())
135 }
136 };
137
138 match rel.r_type() {
140 REL_RELATIVE => unsafe { *target = base.wrapping_add_signed(addend) },
141 REL_SYMBOLIC => unsafe {
142 *target = open_sym()?.reloc_value().wrapping_add_signed(addend)
143 },
144 REL_PLT | REL_GOT => unsafe {
145 let x = open_sym()?.reloc_value();
146 *target = x;
147 },
148 REL_DTPMOD => {
149 let id = if rel.sym() == 0 {
151 lib.tls_id
152 .as_ref()
153 .ok_or_else(|| DynlinkErrorKind::NoTLSInfo {
154 library: lib.name.as_str().into(),
155 })?
156 .tls_id()
157 } else {
158 let other_lib = open_sym()?.lib;
159 other_lib
160 .tls_id
161 .as_ref()
162 .ok_or_else(|| DynlinkErrorKind::NoTLSInfo {
163 library: other_lib.name.as_str().into(),
164 })?
165 .tls_id()
166 };
167 unsafe { *target = id }
168 }
169 REL_DTPOFF => {
170 let val = open_sym().map(|sym| sym.raw_value()).unwrap_or(0);
171 unsafe { *target = val.wrapping_add_signed(addend) }
172 }
173 REL_TPOFF => {
174 let sym = open_sym()?;
175 if let Some(tls) = sym.lib.tls_id {
176 unsafe {
177 *target = sym
178 .raw_value()
179 .wrapping_sub(tls.offset() as u64)
180 .wrapping_add_signed(addend)
181 }
182 } else {
183 error!(
184 "{}: TPOFF relocations require a PT_TLS segment (sym {})",
185 lib, sn
186 );
187 Err(DynlinkErrorKind::NoTLSInfo {
188 library: lib.name.as_str().into(),
189 })?
190 }
191 }
192 _ => {
193 error!("{}: unsupported relocation: {}", lib, rel.r_type());
194 Result::<_, DynlinkError>::Err(
195 DynlinkErrorKind::UnsupportedReloc {
196 library: lib.name.as_str().into(),
197 reloc: rel.r_type().to_string().into(),
198 }
199 .into(),
200 )?
201 }
202 }
203 tracing::trace!("set reloc {} to {:x}", rel.r_type(), unsafe { *target });
204
205 Ok(())
206 }
207}