Skip to main content

relibc/ld_so/
mod.rs

1//! Dynamic loading and linking.
2
3// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
4// dropped from src/lib.rs.
5#![warn(warnings, unused_variables)]
6
7use core::{mem, ptr};
8use object::{
9    Endianness,
10    elf::{self, ProgramHeader32, ProgramHeader64},
11    read::elf::ProgramHeader,
12};
13
14use self::tcb::{Master, Tcb};
15use crate::{
16    header::sys_auxv::{AT_NULL, AT_PHDR, AT_PHENT, AT_PHNUM},
17    platform::{Pal, Sys},
18    start::Stack,
19};
20
21pub const PATH_SEP: char = ':';
22
23mod access;
24pub mod callbacks;
25pub mod debug;
26mod dso;
27pub mod linker;
28pub mod start;
29pub mod tcb;
30
31pub use generic_rt::{ExpectTlsFree, panic_notls};
32
33static mut STATIC_TCB_MASTER: Master = Master {
34    ptr: ptr::null_mut(),
35    image_size: 0,
36    segment_size: 0,
37    offset: 0,
38};
39
40#[inline(never)]
41fn static_init(
42    sp: &'static Stack,
43    #[cfg(target_os = "redox")] thr_fd: redox_rt::proc::FdGuardUpper,
44) {
45    const SIZEOF_PHDR64: usize = mem::size_of::<ProgramHeader64<Endianness>>();
46    const SIZEOF_PHDR32: usize = mem::size_of::<ProgramHeader32<Endianness>>();
47
48    let mut phdr_opt = None;
49    let mut phent_opt = None;
50    let mut phnum_opt = None;
51
52    let mut auxv = sp.auxv();
53    loop {
54        let (kind, value) = unsafe { *auxv };
55        if kind == AT_NULL {
56            break;
57        }
58
59        match kind {
60            AT_PHDR => phdr_opt = Some(value),
61            AT_PHENT => phent_opt = Some(value),
62            AT_PHNUM => phnum_opt = Some(value),
63            _ => (),
64        }
65
66        auxv = unsafe { auxv.add(1) };
67    }
68
69    let phdr = phdr_opt.expect_notls("failed to find AT_PHDR");
70    let phent = phent_opt.expect_notls("failed to find AT_PHENT");
71    let phnum = phnum_opt.expect_notls("failed to find AT_PHNUM");
72
73    for i in 0..phnum {
74        let ph_addr = phdr + phent * i;
75        let endian = Endianness::default();
76        let (p_align, p_filesz, p_memsz, p_type, p_vaddr) = match phent {
77            SIZEOF_PHDR64 => unsafe {
78                let ph = &*(ph_addr as *const ProgramHeader64<Endianness>);
79                (
80                    ph.p_align(endian) as usize,
81                    ph.p_filesz(endian) as usize,
82                    ph.p_memsz(endian) as usize,
83                    ph.p_type(endian),
84                    ph.p_vaddr(endian) as usize,
85                )
86            },
87
88            SIZEOF_PHDR32 => unsafe {
89                let ph = &*(ph_addr as *const ProgramHeader32<Endianness>);
90                (
91                    ph.p_align(endian) as usize,
92                    ph.p_filesz(endian) as usize,
93                    ph.p_memsz(endian) as usize,
94                    ph.p_type(endian),
95                    ph.p_vaddr(endian) as usize,
96                )
97            },
98            _ => panic_notls(format_args!("unknown AT_PHENT size {}", phent)),
99        };
100
101        let page_size = Sys::getpagesize();
102        let voff = p_vaddr % page_size;
103        // let vaddr = ph.p_vaddr as usize - voff;
104        let vsize = (p_memsz + voff).div_ceil(page_size) * page_size;
105
106        if p_type == elf::PT_TLS {
107            let valign = if p_align > 0 {
108                p_memsz.div_ceil(p_align) * p_align
109            } else {
110                p_memsz
111            };
112
113            unsafe {
114                STATIC_TCB_MASTER.ptr = p_vaddr as *const u8;
115                STATIC_TCB_MASTER.image_size = p_filesz;
116                STATIC_TCB_MASTER.offset = valign;
117
118                let tcb = Tcb::new(vsize).expect_notls("failed to allocate TCB");
119                tcb.masters_ptr = ptr::addr_of_mut!(STATIC_TCB_MASTER);
120                tcb.masters_len = mem::size_of::<Master>();
121                tcb.copy_masters()
122                    .expect_notls("failed to copy TLS master data");
123                tcb.activate(
124                    #[cfg(target_os = "redox")]
125                    Some(thr_fd),
126                );
127            }
128
129            //TODO: Warning on multiple TLS sections?
130            return;
131        }
132    }
133}
134
135#[cfg(any(target_os = "linux", target_os = "redox"))]
136pub unsafe fn init(
137    sp: &'static Stack,
138    #[cfg(target_os = "redox")] thr_fd: redox_rt::proc::FdGuardUpper,
139) {
140    let tp: usize;
141
142    #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
143    {
144        const ARCH_GET_FS: usize = 0x1003;
145        let mut val = 0usize;
146        syscall!(ARCH_PRCTL, ARCH_GET_FS, &raw mut val);
147        tp = val;
148    }
149    #[cfg(target_arch = "aarch64")]
150    unsafe {
151        core::arch::asm!(
152            "mrs {}, tpidr_el0",
153            out(reg) tp,
154        );
155    }
156    #[cfg(all(target_os = "redox", target_arch = "x86"))]
157    {
158        let mut env = syscall::EnvRegisters::default();
159
160        {
161            let file = thr_fd
162                .dup_into_upper(b"regs/env")
163                .expect_notls("failed to open handle for process registers");
164
165            file.read(&mut env).expect_notls("failed to read gsbase");
166        }
167
168        tp = env.gsbase as usize;
169    }
170    #[cfg(all(target_os = "redox", target_arch = "x86_64"))]
171    {
172        let mut env = syscall::EnvRegisters::default();
173
174        {
175            let file = thr_fd
176                .dup_into_upper(b"regs/env")
177                .expect_notls("failed to open handle for process registers");
178
179            file.read(&mut env).expect_notls("failed to read fsbase");
180        }
181
182        tp = env.fsbase as usize;
183    }
184    #[cfg(all(target_os = "redox", target_arch = "riscv64"))]
185    unsafe {
186        core::arch::asm!(
187            "mv {}, tp",
188            out(reg) tp,
189        );
190    }
191
192    if tp == 0 {
193        static_init(
194            sp,
195            #[cfg(target_os = "redox")]
196            thr_fd,
197        );
198    } else {
199        // The thread fd must already be present in the already existing TCB. Don't close it.
200        #[cfg(target_os = "redox")]
201        core::mem::forget(thr_fd);
202    }
203}
204
205pub unsafe fn fini() {
206    if let Some(tcb) = unsafe { Tcb::current() }
207        && !tcb.linker_ptr.is_null()
208    {
209        let linker = unsafe { (*tcb.linker_ptr).lock() };
210        linker.fini();
211    }
212}