dynlink/arch/
x86_64.rs

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
28/// Return the TLS variant defined by the arch-specific ABI.
29pub fn get_tls_variant() -> TlsVariant {
30    TlsVariant::Variant2
31}
32
33/// Get a pointer to the current thread control block, using the thread pointer.
34///
35/// # Safety
36/// The TCB must actually contain runtime data of type T, and be initialized.
37pub 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    /// Get a pointer to the thread control block for this TLS region.
45    ///
46    /// # Safety
47    /// The TCB must actually contain runtime data of type T, and be initialized.
48    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        // Lookup a symbol if the relocation's symbol index is non-zero.
66        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        // Helper for logging errors.
84        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        // This is where the magic happens.
108        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                // See the TLS module for understanding where the TLS ID is coming from.
116                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}