xref: /relibc/src/ld_so/tcb.rs (revision a7480ea65652f7bb2211c2a70623165ff2e3bd7f)
1 use alloc::vec::Vec;
2 use core::{mem, ptr, slice};
3 use goblin::error::{Error, Result};
4 
5 use crate::{header::sys_mman, ld_so::linker::Linker, sync::mutex::Mutex};
6 
7 use super::PAGE_SIZE;
8 
9 #[repr(C)]
10 #[derive(Debug)]
11 pub struct Master {
12     /// Pointer to initial data
13     pub ptr: *const u8,
14     /// Length of initial data in bytes
15     pub len: usize,
16     /// Offset in TLS to copy initial data to
17     pub offset: usize,
18 }
19 
20 impl Master {
21     /// The initial data for this TLS region
22     pub unsafe fn data(&self) -> &'static [u8] {
23         slice::from_raw_parts(self.ptr, self.len)
24     }
25 }
26 
27 #[derive(Debug)]
28 #[repr(C)]
29 pub struct Tcb {
30     /// Pointer to the end of static TLS. Must be the first member
31     pub tls_end: *mut u8,
32     /// Size of the memory allocated for the static TLS in bytes (multiple of PAGE_SIZE)
33     pub tls_len: usize,
34     /// Pointer to this structure
35     pub tcb_ptr: *mut Tcb,
36     /// Size of the memory allocated for this structure in bytes (should be PAGE_SIZE)
37     pub tcb_len: usize,
38     /// Pointer to a list of initial TLS data
39     pub masters_ptr: *mut Master,
40     /// Size of the masters list in bytes (multiple of mem::size_of::<Master>())
41     pub masters_len: usize,
42     /// Index of last copied Master
43     pub last_master_copied: usize,
44     /// Pointer to dynamic linker
45     pub linker_ptr: *const Mutex<Linker>,
46     /// pointer to rust memory allocator structure
47     pub mspace: usize,
48 }
49 
50 impl Tcb {
51     /// Create a new TCB
52     pub unsafe fn new(size: usize) -> Result<&'static mut Self> {
53         let (tls, tcb_page) = Self::os_new(round_up(size, PAGE_SIZE))?;
54 
55         let tcb_ptr = tcb_page.as_mut_ptr() as *mut Self;
56         trace!("New TCB: {:p}", tcb_ptr);
57         ptr::write(
58             tcb_ptr,
59             Self {
60                 tls_end: tls.as_mut_ptr().add(tls.len()),
61                 tls_len: tls.len(),
62                 tcb_ptr,
63                 tcb_len: tcb_page.len(),
64                 masters_ptr: ptr::null_mut(),
65                 masters_len: 0,
66                 last_master_copied: 0,
67                 linker_ptr: ptr::null(),
68                 mspace: 0,
69             },
70         );
71 
72         Ok(&mut *tcb_ptr)
73     }
74 
75     /// Get the current TCB
76     pub unsafe fn current() -> Option<&'static mut Self> {
77         let tcb_ptr = Self::arch_read(offset_of!(Self, tcb_ptr)) as *mut Self;
78         let tcb_len = Self::arch_read(offset_of!(Self, tcb_len));
79         if tcb_ptr.is_null() || tcb_len < mem::size_of::<Self>() {
80             None
81         } else {
82             Some(&mut *tcb_ptr)
83         }
84     }
85 
86     /// A slice for all of the TLS data
87     pub unsafe fn tls(&self) -> Option<&'static mut [u8]> {
88         if self.tls_end.is_null() || self.tls_len == 0 {
89             None
90         } else {
91             Some(slice::from_raw_parts_mut(
92                 self.tls_end.offset(-(self.tls_len as isize)),
93                 self.tls_len,
94             ))
95         }
96     }
97 
98     /// The initial images for TLS
99     pub unsafe fn masters(&self) -> Option<&'static mut [Master]> {
100         if self.masters_ptr.is_null() || self.masters_len == 0 {
101             None
102         } else {
103             Some(slice::from_raw_parts_mut(
104                 self.masters_ptr,
105                 self.masters_len / mem::size_of::<Master>(),
106             ))
107         }
108     }
109 
110     /// Copy data from masters
111     pub unsafe fn copy_masters(&mut self) -> Result<()> {
112         //TODO: Complain if masters or tls exist without the other
113         if let Some(tls) = self.tls() {
114             if let Some(masters) = self.masters() {
115                 for (i, master) in masters
116                     .iter()
117                     .skip(self.last_master_copied)
118                     .filter(|m| m.len > 0)
119                     .enumerate()
120                 {
121                     let range =
122                         self.tls_len - master.offset..self.tls_len - master.offset + master.len;
123                     if let Some(tls_data) = tls.get_mut(range) {
124                         let data = master.data();
125                         trace!(
126                             "tls master {}: {:p}, {:#x}: {:p}, {:#x}",
127                             i,
128                             data.as_ptr(),
129                             data.len(),
130                             tls_data.as_mut_ptr(),
131                             tls_data.len()
132                         );
133                         tls_data.copy_from_slice(data);
134                     } else {
135                         return Err(Error::Malformed(format!("failed to copy tls master {}", i)));
136                     }
137                 }
138                 self.last_master_copied = masters.len();
139             }
140         }
141 
142         Ok(())
143     }
144 
145     /// The initial images for TLS
146     pub unsafe fn append_masters(&mut self, mut new_masters: Vec<Master>) {
147         if self.masters_ptr.is_null() {
148             self.masters_ptr = new_masters.as_mut_ptr();
149             self.masters_len = new_masters.len() * mem::size_of::<Master>();
150             mem::forget(new_masters);
151         } else {
152             let len = self.masters_len / mem::size_of::<Master>();
153             let mut masters = Vec::from_raw_parts(self.masters_ptr, len, len);
154             masters.extend(new_masters.into_iter());
155             self.masters_ptr = masters.as_mut_ptr();
156             self.masters_len = masters.len() * mem::size_of::<Master>();
157             mem::forget(masters);
158         }
159     }
160 
161     /// Activate TLS
162     pub unsafe fn activate(&mut self) {
163         Self::os_arch_activate(self.tcb_ptr as usize);
164     }
165 
166     /// Mapping with correct flags for TCB and TLS
167     unsafe fn map(size: usize) -> Result<&'static mut [u8]> {
168         let ptr = sys_mman::mmap(
169             ptr::null_mut(),
170             size,
171             sys_mman::PROT_READ | sys_mman::PROT_WRITE,
172             sys_mman::MAP_ANONYMOUS | sys_mman::MAP_PRIVATE,
173             -1,
174             0,
175         );
176         if ptr as usize == !0
177         /* MAP_FAILED */
178         {
179             return Err(Error::Malformed(format!("failed to map tls")));
180         }
181         ptr::write_bytes(ptr as *mut u8, 0, size);
182         Ok(slice::from_raw_parts_mut(ptr as *mut u8, size))
183     }
184 
185     /// OS specific code to create a new TLS and TCB - Linux
186     #[cfg(target_os = "linux")]
187     unsafe fn os_new(size: usize) -> Result<(&'static mut [u8], &'static mut [u8])> {
188         let tls_tcb = Self::map(size + PAGE_SIZE)?;
189         Ok(tls_tcb.split_at_mut(size))
190     }
191 
192     /// OS specific code to create a new TLS and TCB - Redox
193     #[cfg(target_os = "redox")]
194     unsafe fn os_new(size: usize) -> Result<(&'static mut [u8], &'static mut [u8])> {
195         use crate::header::unistd;
196         //TODO: better method of finding fs offset
197         let pid = unistd::getpid();
198         let tcb_addr = 0xB000_0000 + pid as usize * PAGE_SIZE;
199         let tls = Self::map(size)?;
200         Ok((
201             tls,
202             //TODO: Consider allocating TCB as part of TLS
203             slice::from_raw_parts_mut(tcb_addr as *mut u8, PAGE_SIZE),
204         ))
205     }
206 
207     /// Architecture specific code to read a usize from the TCB - x86_64
208     #[inline(always)]
209     #[cfg(target_arch = "x86_64")]
210     unsafe fn arch_read(offset: usize) -> usize {
211         let value;
212         llvm_asm!("
213             mov rax, fs:[rdi]
214             "
215             : "={rax}"(value)
216             : "{rdi}"(offset)
217             :
218             : "intel"
219         );
220         value
221     }
222 
223     /// OS and architecture specific code to activate TLS - Linux x86_64
224     #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
225     unsafe fn os_arch_activate(tp: usize) {
226         const ARCH_SET_FS: usize = 0x1002;
227         syscall!(ARCH_PRCTL, ARCH_SET_FS, tp);
228     }
229 
230     /// OS and architecture specific code to activate TLS - Linux x86_64
231     #[cfg(all(target_os = "redox", target_arch = "x86_64"))]
232     unsafe fn os_arch_activate(tp: usize) {
233         //TODO: Consider setting FS offset to TCB pointer
234     }
235 }
236 
237 pub fn round_up(value: usize, alignment: usize) -> usize {
238     return (value + alignment - 1) & (!(alignment - 1));
239 }
240