1 //! TLS utilities.
2 //!
3 //! # Safety
4 //!
5 //! This file contains code that reads the raw phdr array pointed to by the
6 //! kernel-provided AUXV values.
7 #![allow(unsafe_code)]
8 
9 use crate::backend::c;
10 use crate::backend::param::auxv::exe_phdrs;
11 use core::arch::global_asm;
12 use core::ptr::{null, NonNull};
13 use linux_raw_sys::elf::*;
14 
15 /// For use with [`set_thread_area`].
16 ///
17 /// [`set_thread_area`]: crate::runtime::set_thread_area
18 #[cfg(target_arch = "x86")]
19 pub type UserDesc = linux_raw_sys::general::user_desc;
20 
startup_tls_info() -> StartupTlsInfo21 pub(crate) fn startup_tls_info() -> StartupTlsInfo {
22     let mut base = null();
23     let mut tls_phdr = null();
24     let mut stack_size = 0;
25 
26     let (first_phdr, phent, phnum) = exe_phdrs();
27     let mut current_phdr = first_phdr.cast::<Elf_Phdr>();
28 
29     // The dynamic address of the dynamic section, which we can compare with
30     // the `PT_DYNAMIC` header's static address, if present.
31     let dynamic_addr: *const c::c_void = unsafe { &_DYNAMIC };
32 
33     // SAFETY: We assume the phdr array pointer and length the kernel provided
34     // to the process describe a valid phdr array.
35     unsafe {
36         let phdrs_end = current_phdr.cast::<u8>().add(phnum * phent).cast();
37         while current_phdr != phdrs_end {
38             let phdr = &*current_phdr;
39             current_phdr = current_phdr.cast::<u8>().add(phent).cast();
40 
41             match phdr.p_type {
42                 // Compute the offset from the static virtual addresses
43                 // in the `p_vaddr` fields to the dynamic addresses. We don't
44                 // always get a `PT_PHDR` or `PT_DYNAMIC` header, so use
45                 // whichever one we get.
46                 PT_PHDR => base = first_phdr.cast::<u8>().wrapping_sub(phdr.p_vaddr),
47                 PT_DYNAMIC => base = dynamic_addr.cast::<u8>().wrapping_sub(phdr.p_vaddr),
48 
49                 PT_TLS => tls_phdr = phdr,
50                 PT_GNU_STACK => stack_size = phdr.p_memsz,
51                 _ => {}
52             }
53         }
54 
55         if tls_phdr.is_null() {
56             StartupTlsInfo {
57                 addr: NonNull::dangling().as_ptr(),
58                 mem_size: 0,
59                 file_size: 0,
60                 align: 1,
61                 stack_size: 0,
62             }
63         } else {
64             StartupTlsInfo {
65                 addr: base.cast::<u8>().wrapping_add((*tls_phdr).p_vaddr).cast(),
66                 mem_size: (*tls_phdr).p_memsz,
67                 file_size: (*tls_phdr).p_filesz,
68                 align: (*tls_phdr).p_align,
69                 stack_size,
70             }
71         }
72     }
73 }
74 
75 extern "C" {
76     /// Declare the `_DYNAMIC` symbol so that we can compare its address with
77     /// the static address in the `PT_DYNAMIC` header to learn our offset. Use
78     /// a weak symbol because `_DYNAMIC` is not always present.
79     static _DYNAMIC: c::c_void;
80 }
81 // Rust has `extern_weak` but it isn't stable, so use a `global_asm`.
82 global_asm!(".weak _DYNAMIC");
83 
84 /// The values returned from [`startup_tls_info`].
85 ///
86 /// [`startup_tls_info`]: crate::runtime::startup_tls_info
87 pub struct StartupTlsInfo {
88     /// The base address of the TLS segment.
89     pub addr: *const c::c_void,
90     /// The size of the memory region.
91     pub mem_size: usize,
92     /// The size beyond which all memory is zero-initialized.
93     pub file_size: usize,
94     /// The required alignment for the TLS segment.
95     pub align: usize,
96     /// The requested minimum size for stacks.
97     pub stack_size: usize,
98 }
99