dynlink/arch/
x86_64.rs

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