xref: /relibc/src/ld_so/mod.rs (revision 7d27737c3ffa5bbdf5d00592bb95abf1f9703aee)
1 use core::{mem, ptr};
2 use goblin::elf::program_header::{self, program_header32, program_header64, ProgramHeader};
3 
4 use self::tcb::{Master, Tcb};
5 use crate::{
6     header::sys_auxv::AT_NULL,
7     platform::{Pal, Sys},
8     start::Stack,
9 };
10 
11 #[cfg(target_os = "redox")]
12 pub const PATH_SEP: char = ';';
13 
14 #[cfg(target_os = "linux")]
15 pub const PATH_SEP: char = ':';
16 
17 mod access;
18 pub mod callbacks;
19 pub mod debug;
20 mod dso;
21 pub mod linker;
22 pub mod start;
23 pub mod tcb;
24 
25 static mut STATIC_TCB_MASTER: Master = Master {
26     ptr: ptr::null_mut(),
27     len: 0,
28     offset: 0,
29 };
30 
31 fn panic_notls(msg: impl core::fmt::Display) -> ! {
32     eprintln!("panicked in ld.so: {}", msg);
33 
34     unsafe {
35         core::intrinsics::abort();
36     }
37 }
38 
39 pub trait ExpectTlsFree {
40     type Unwrapped;
41 
42     fn expect_notls(self, msg: &str) -> Self::Unwrapped;
43 }
44 impl<T, E: core::fmt::Debug> ExpectTlsFree for Result<T, E> {
45     type Unwrapped = T;
46 
47     fn expect_notls(self, msg: &str) -> T {
48         match self {
49             Ok(t) => t,
50             Err(err) => panic_notls(format_args!(
51                 "{}: expect failed for Result with err: {:?}",
52                 msg, err
53             )),
54         }
55     }
56 }
57 impl<T> ExpectTlsFree for Option<T> {
58     type Unwrapped = T;
59 
60     fn expect_notls(self, msg: &str) -> T {
61         match self {
62             Some(t) => t,
63             None => panic_notls(format_args!("{}: expect failed for Option", msg)),
64         }
65     }
66 }
67 
68 #[inline(never)]
69 pub fn static_init(sp: &'static Stack) {
70     let mut phdr_opt = None;
71     let mut phent_opt = None;
72     let mut phnum_opt = None;
73 
74     let mut auxv = sp.auxv();
75     loop {
76         let (kind, value) = unsafe { *auxv };
77         if kind == AT_NULL {
78             break;
79         }
80 
81         match kind {
82             3 => phdr_opt = Some(value),
83             4 => phent_opt = Some(value),
84             5 => phnum_opt = Some(value),
85             _ => (),
86         }
87 
88         auxv = unsafe { auxv.add(1) };
89     }
90 
91     let phdr = phdr_opt.expect_notls("failed to find AT_PHDR");
92     let phent = phent_opt.expect_notls("failed to find AT_PHENT");
93     let phnum = phnum_opt.expect_notls("failed to find AT_PHNUM");
94 
95     for i in 0..phnum {
96         let ph_addr = phdr + phent * i;
97         let ph: ProgramHeader = match phent {
98             program_header32::SIZEOF_PHDR => {
99                 unsafe { *(ph_addr as *const program_header32::ProgramHeader) }.into()
100             }
101             program_header64::SIZEOF_PHDR => {
102                 unsafe { *(ph_addr as *const program_header64::ProgramHeader) }.into()
103             }
104             _ => panic_notls(format_args!("unknown AT_PHENT size {}", phent)),
105         };
106 
107         let page_size = Sys::getpagesize();
108         let voff = ph.p_vaddr as usize % page_size;
109         let vaddr = ph.p_vaddr as usize - voff;
110         let vsize = ((ph.p_memsz as usize + voff + page_size - 1) / page_size) * page_size;
111 
112         match ph.p_type {
113             program_header::PT_TLS => {
114                 let valign = if ph.p_align > 0 {
115                     ((ph.p_memsz + (ph.p_align - 1)) / ph.p_align) * ph.p_align
116                 } else {
117                     ph.p_memsz
118                 } as usize;
119 
120                 unsafe {
121                     STATIC_TCB_MASTER.ptr = ph.p_vaddr as usize as *const u8;
122                     STATIC_TCB_MASTER.len = ph.p_filesz as usize;
123                     STATIC_TCB_MASTER.offset = valign;
124 
125                     let tcb = Tcb::new(vsize).expect_notls("failed to allocate TCB");
126                     tcb.masters_ptr = &mut STATIC_TCB_MASTER;
127                     tcb.masters_len = mem::size_of::<Master>();
128                     tcb.copy_masters()
129                         .expect_notls("failed to copy TLS master data");
130                     tcb.activate();
131                 }
132 
133                 //TODO: Warning on multiple TLS sections?
134                 return;
135             }
136             _ => (),
137         }
138     }
139 }
140 
141 #[cfg(any(target_os = "linux", target_os = "redox"))]
142 pub unsafe fn init(sp: &'static Stack) {
143     let mut tp = 0usize;
144 
145     #[cfg(target_os = "linux")]
146     {
147         const ARCH_GET_FS: usize = 0x1003;
148         syscall!(ARCH_PRCTL, ARCH_GET_FS, &mut tp as *mut usize);
149     }
150     #[cfg(all(target_os = "redox", target_arch = "aarch64"))]
151     {
152         core::arch::asm!(
153             "mrs {}, tpidr_el0",
154             out(reg) tp,
155         );
156     }
157     #[cfg(all(target_os = "redox", target_arch = "x86"))]
158     {
159         let mut env = syscall::EnvRegisters::default();
160 
161         let file = syscall::open(
162             "thisproc:current/regs/env",
163             syscall::O_CLOEXEC | syscall::O_RDONLY,
164         )
165         .expect_notls("failed to open handle for process registers");
166 
167         let _ = syscall::read(file, &mut env).expect_notls("failed to read gsbase");
168 
169         let _ = syscall::close(file);
170 
171         tp = env.gsbase as usize;
172     }
173     #[cfg(all(target_os = "redox", target_arch = "x86_64"))]
174     {
175         let mut env = syscall::EnvRegisters::default();
176 
177         let file = syscall::open(
178             "thisproc:current/regs/env",
179             syscall::O_CLOEXEC | syscall::O_RDONLY,
180         )
181         .expect_notls("failed to open handle for process registers");
182 
183         let _ = syscall::read(file, &mut env).expect_notls("failed to read fsbase");
184 
185         let _ = syscall::close(file);
186 
187         tp = env.fsbase as usize;
188     }
189 
190     if tp == 0 {
191         static_init(sp);
192     }
193 }
194 
195 pub unsafe fn fini() {
196     if let Some(tcb) = Tcb::current() {
197         if tcb.linker_ptr != ptr::null_mut() {
198             let linker = (&*tcb.linker_ptr).lock();
199             linker.fini();
200         }
201     }
202 }
203