xref: /relibc/src/platform/redox/clone.rs (revision a37916101fbe8061704c1fc5079021b20789e318)
1 use core::{arch::global_asm, mem::size_of};
2 
3 use alloc::{boxed::Box, vec::Vec};
4 
5 use syscall::{
6     data::Map,
7     error::{Error, Result, EINVAL, ENAMETOOLONG},
8     flag::{MapFlags, O_CLOEXEC},
9     SIGCONT,
10 };
11 
12 use super::extra::{create_set_addr_space_buf, FdGuard};
13 
14 pub use redox_exec::*;
15 
16 /// Spawns a new context sharing the same address space as the current one (i.e. a new thread).
17 pub unsafe fn pte_clone_impl(stack: *mut usize) -> Result<usize> {
18     let cur_pid_fd = FdGuard::new(syscall::open("thisproc:current/open_via_dup", O_CLOEXEC)?);
19     let (new_pid_fd, new_pid) = new_context()?;
20 
21     // Allocate a new signal stack.
22     {
23         let sigstack_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"sigstack")?);
24 
25         const SIGSTACK_SIZE: usize = 1024 * 256;
26 
27         // TODO: Put sigstack at high addresses?
28         let target_sigstack = syscall::fmap(
29             !0,
30             &Map {
31                 address: 0,
32                 flags: MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE,
33                 offset: 0,
34                 size: SIGSTACK_SIZE,
35             },
36         )? + SIGSTACK_SIZE;
37 
38         let _ = syscall::write(*sigstack_fd, &usize::to_ne_bytes(target_sigstack))?;
39     }
40 
41     copy_str(*cur_pid_fd, *new_pid_fd, "name")?;
42 
43     // Reuse existing address space
44     {
45         let cur_addr_space_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"addrspace")?);
46         let new_addr_space_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-addrspace")?);
47 
48         let buf = create_set_addr_space_buf(
49             *cur_addr_space_fd,
50             __relibc_internal_pte_clone_ret as usize,
51             stack as usize,
52         );
53         let _ = syscall::write(*new_addr_space_sel_fd, &buf)?;
54     }
55 
56     // Reuse file table
57     {
58         let cur_filetable_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"filetable")?);
59         let new_filetable_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-filetable")?);
60 
61         let _ = syscall::write(
62             *new_filetable_sel_fd,
63             &usize::to_ne_bytes(*cur_filetable_fd),
64         )?;
65     }
66 
67     // Reuse sigactions (on Linux, CLONE_THREAD requires CLONE_SIGHAND which implies the sigactions
68     // table is reused).
69     {
70         let cur_sigaction_fd = FdGuard::new(syscall::dup(*cur_pid_fd, b"sigactions")?);
71         let new_sigaction_sel_fd = FdGuard::new(syscall::dup(*new_pid_fd, b"current-sigactions")?);
72 
73         let _ = syscall::write(
74             *new_sigaction_sel_fd,
75             &usize::to_ne_bytes(*cur_sigaction_fd),
76         )?;
77     }
78 
79     copy_env_regs(*cur_pid_fd, *new_pid_fd)?;
80 
81     // Unblock context.
82     syscall::kill(new_pid, SIGCONT)?;
83     let _ = syscall::waitpid(new_pid, &mut 0, syscall::WUNTRACED | syscall::WCONTINUED);
84 
85     Ok(new_pid)
86 }
87 
88 extern "C" {
89     fn __relibc_internal_pte_clone_ret();
90 }
91 
92 #[cfg(target_arch = "aarch64")]
93 core::arch::global_asm!(
94     "
95     .globl __relibc_internal_pte_clone_ret
96     .type __relibc_internal_pte_clone_ret, @function
97     .p2align 6
98 __relibc_internal_pte_clone_ret:
99     # Load registers
100     ldr x8, [sp], #8
101     ldr x0, [sp], #8
102     ldr x1, [sp], #8
103     ldr x2, [sp], #8
104     ldr x3, [sp], #8
105     ldr x4, [sp], #8
106     ldr x5, [sp], #8
107 
108     # Call entry point
109     blr x8
110 
111     ret
112     .size __relibc_internal_pte_clone_ret, . - __relibc_internal_pte_clone_ret
113 "
114 );
115 
116 #[cfg(target_arch = "x86")]
117 core::arch::global_asm!(
118     "
119     .globl __relibc_internal_pte_clone_ret
120     .type __relibc_internal_pte_clone_ret, @function
121     .p2align 6
122 __relibc_internal_pte_clone_ret:
123     # Load registers
124     pop eax
125 
126     sub esp, 8
127 
128     mov DWORD PTR [esp], 0x00001F80
129     # TODO: ldmxcsr [esp]
130     mov WORD PTR [esp], 0x037F
131     fldcw [esp]
132 
133     add esp, 8
134 
135     # Call entry point
136     call eax
137 
138     ret
139     .size __relibc_internal_pte_clone_ret, . - __relibc_internal_pte_clone_ret
140 "
141 );
142 
143 #[cfg(target_arch = "x86_64")]
144 core::arch::global_asm!(
145     "
146     .globl __relibc_internal_pte_clone_ret
147     .type __relibc_internal_pte_clone_ret, @function
148     .p2align 6
149 __relibc_internal_pte_clone_ret:
150     # Load registers
151     pop rax
152     pop rdi
153     pop rsi
154     pop rdx
155     pop rcx
156     pop r8
157     pop r9
158 
159     sub rsp, 8
160 
161     mov DWORD PTR [rsp], 0x00001F80
162     ldmxcsr [rsp]
163     mov WORD PTR [rsp], 0x037F
164     fldcw [rsp]
165 
166     add rsp, 8
167 
168     # Call entry point
169     call rax
170 
171     ret
172     .size __relibc_internal_pte_clone_ret, . - __relibc_internal_pte_clone_ret
173 "
174 );
175