//! TLS utilities. //! //! # Safety //! //! This file contains code that reads the raw phdr array pointed to by the //! kernel-provided AUXV values. #![allow(unsafe_code)] use crate::backend::c; use crate::backend::param::auxv::exe_phdrs; use core::arch::global_asm; use core::ptr::{null, NonNull}; use linux_raw_sys::elf::*; /// For use with [`set_thread_area`]. /// /// [`set_thread_area`]: crate::runtime::set_thread_area #[cfg(target_arch = "x86")] pub type UserDesc = linux_raw_sys::general::user_desc; pub(crate) fn startup_tls_info() -> StartupTlsInfo { let mut base = null(); let mut tls_phdr = null(); let mut stack_size = 0; let (first_phdr, phent, phnum) = exe_phdrs(); let mut current_phdr = first_phdr.cast::(); // The dynamic address of the dynamic section, which we can compare with // the `PT_DYNAMIC` header's static address, if present. let dynamic_addr: *const c::c_void = unsafe { &_DYNAMIC }; // SAFETY: We assume the phdr array pointer and length the kernel provided // to the process describe a valid phdr array. unsafe { let phdrs_end = current_phdr.cast::().add(phnum * phent).cast(); while current_phdr != phdrs_end { let phdr = &*current_phdr; current_phdr = current_phdr.cast::().add(phent).cast(); match phdr.p_type { // Compute the offset from the static virtual addresses // in the `p_vaddr` fields to the dynamic addresses. We don't // always get a `PT_PHDR` or `PT_DYNAMIC` header, so use // whichever one we get. PT_PHDR => base = first_phdr.cast::().wrapping_sub(phdr.p_vaddr), PT_DYNAMIC => base = dynamic_addr.cast::().wrapping_sub(phdr.p_vaddr), PT_TLS => tls_phdr = phdr, PT_GNU_STACK => stack_size = phdr.p_memsz, _ => {} } } if tls_phdr.is_null() { StartupTlsInfo { addr: NonNull::dangling().as_ptr(), mem_size: 0, file_size: 0, align: 1, stack_size: 0, } } else { StartupTlsInfo { addr: base.cast::().wrapping_add((*tls_phdr).p_vaddr).cast(), mem_size: (*tls_phdr).p_memsz, file_size: (*tls_phdr).p_filesz, align: (*tls_phdr).p_align, stack_size, } } } } extern "C" { /// Declare the `_DYNAMIC` symbol so that we can compare its address with /// the static address in the `PT_DYNAMIC` header to learn our offset. Use /// a weak symbol because `_DYNAMIC` is not always present. static _DYNAMIC: c::c_void; } // Rust has `extern_weak` but it isn't stable, so use a `global_asm`. global_asm!(".weak _DYNAMIC"); /// The values returned from [`startup_tls_info`]. /// /// [`startup_tls_info`]: crate::runtime::startup_tls_info pub struct StartupTlsInfo { /// The base address of the TLS segment. pub addr: *const c::c_void, /// The size of the memory region. pub mem_size: usize, /// The size beyond which all memory is zero-initialized. pub file_size: usize, /// The required alignment for the TLS segment. pub align: usize, /// The requested minimum size for stacks. pub stack_size: usize, }