1 //! Architecture-specific syscall code. 2 //! 3 //! This module also has a `choose` submodule which chooses a scheme and is 4 //! what most of the `rustix` syscalls use. 5 //! 6 //! Compilers should really have intrinsics for making system calls. They're 7 //! much like regular calls, with custom calling conventions, and calling 8 //! conventions are otherwise the compiler's job. But for now, use inline asm. 9 //! 10 //! The calling conventions for Linux syscalls are [documented here]. 11 //! 12 //! [documented here]: https://man7.org/linux/man-pages/man2/syscall.2.html 13 //! 14 //! # Safety 15 //! 16 //! This contains the inline `asm` statements performing the syscall 17 //! instructions. 18 19 #![allow(unsafe_code)] 20 #![cfg_attr(not(feature = "all-apis"), allow(unused_imports))] 21 // We'll use as many arguments as syscalls need. 22 #![allow(clippy::too_many_arguments)] 23 24 // These functions always use the machine's syscall instruction, even when it 25 // isn't the fastest option available. 26 #[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] 27 #[cfg_attr(all(target_arch = "arm", not(thumb_mode)), path = "arm.rs")] 28 #[cfg_attr(all(target_arch = "arm", thumb_mode), path = "thumb.rs")] 29 #[cfg_attr(target_arch = "mips", path = "mips.rs")] 30 #[cfg_attr(target_arch = "mips32r6", path = "mips32r6.rs")] 31 #[cfg_attr(target_arch = "mips64", path = "mips64.rs")] 32 #[cfg_attr(target_arch = "mips64r6", path = "mips64r6.rs")] 33 #[cfg_attr(target_arch = "powerpc64", path = "powerpc64.rs")] 34 #[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")] 35 #[cfg_attr(target_arch = "x86", path = "x86.rs")] 36 #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] 37 pub(in crate::backend) mod asm; 38 39 // On most architectures, the architecture syscall instruction is fast, so use 40 // it directly. 41 #[cfg(any( 42 target_arch = "arm", 43 target_arch = "aarch64", 44 target_arch = "mips", 45 target_arch = "mips32r6", 46 target_arch = "mips64", 47 target_arch = "mips64r6", 48 target_arch = "powerpc64", 49 target_arch = "riscv64", 50 target_arch = "x86_64", 51 ))] 52 pub(in crate::backend) use self::asm as choose; 53 54 // On 32-bit x86, use vDSO wrappers for all syscalls. We could use the 55 // architecture syscall instruction (`int 0x80`), but the vDSO kernel_vsyscall 56 // mechanism is much faster. 57 #[cfg(target_arch = "x86")] 58 pub(in crate::backend) use super::vdso_wrappers::x86_via_vdso as choose; 59 60 // This would be the code for always using `int 0x80` on 32-bit x86. 61 //#[cfg(target_arch = "x86")] 62 //pub(in crate::backend) use self::asm as choose; 63 64 // Macros for invoking system calls. 65 // 66 // These factor out: 67 // - Calling `nr` on the syscall number to convert it into `SyscallNumber`. 68 // - Calling `.into()` on each of the arguments to convert them into `ArgReg`. 69 // - Qualifying the `syscall*` and `__NR_*` identifiers. 70 // - Counting the number of arguments. 71 macro_rules! syscall { 72 ($nr:ident) => { 73 $crate::backend::arch::choose::syscall0($crate::backend::reg::nr( 74 linux_raw_sys::general::$nr, 75 )) 76 }; 77 78 ($nr:ident, $a0:expr) => { 79 $crate::backend::arch::choose::syscall1( 80 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 81 $a0.into(), 82 ) 83 }; 84 85 ($nr:ident, $a0:expr, $a1:expr) => { 86 $crate::backend::arch::choose::syscall2( 87 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 88 $a0.into(), 89 $a1.into(), 90 ) 91 }; 92 93 ($nr:ident, $a0:expr, $a1:expr, $a2:expr) => { 94 $crate::backend::arch::choose::syscall3( 95 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 96 $a0.into(), 97 $a1.into(), 98 $a2.into(), 99 ) 100 }; 101 102 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => { 103 $crate::backend::arch::choose::syscall4( 104 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 105 $a0.into(), 106 $a1.into(), 107 $a2.into(), 108 $a3.into(), 109 ) 110 }; 111 112 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => { 113 $crate::backend::arch::choose::syscall5( 114 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 115 $a0.into(), 116 $a1.into(), 117 $a2.into(), 118 $a3.into(), 119 $a4.into(), 120 ) 121 }; 122 123 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => { 124 $crate::backend::arch::choose::syscall6( 125 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 126 $a0.into(), 127 $a1.into(), 128 $a2.into(), 129 $a3.into(), 130 $a4.into(), 131 $a5.into(), 132 ) 133 }; 134 135 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => { 136 $crate::backend::arch::choose::syscall7( 137 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 138 $a0.into(), 139 $a1.into(), 140 $a2.into(), 141 $a3.into(), 142 $a4.into(), 143 $a5.into(), 144 $a6.into(), 145 ) 146 }; 147 } 148 149 // Macro to invoke a syscall that always uses direct assembly, rather than the 150 // vDSO. Useful when still finding the vDSO. 151 #[allow(unused_macros)] 152 macro_rules! syscall_always_asm { 153 ($nr:ident) => { 154 $crate::backend::arch::asm::syscall0($crate::backend::reg::nr(linux_raw_sys::general::$nr)) 155 }; 156 157 ($nr:ident, $a0:expr) => { 158 $crate::backend::arch::asm::syscall1( 159 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 160 $a0.into(), 161 ) 162 }; 163 164 ($nr:ident, $a0:expr, $a1:expr) => { 165 $crate::backend::arch::asm::syscall2( 166 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 167 $a0.into(), 168 $a1.into(), 169 ) 170 }; 171 172 ($nr:ident, $a0:expr, $a1:expr, $a2:expr) => { 173 $crate::backend::arch::asm::syscall3( 174 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 175 $a0.into(), 176 $a1.into(), 177 $a2.into(), 178 ) 179 }; 180 181 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => { 182 $crate::backend::arch::asm::syscall4( 183 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 184 $a0.into(), 185 $a1.into(), 186 $a2.into(), 187 $a3.into(), 188 ) 189 }; 190 191 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => { 192 $crate::backend::arch::asm::syscall5( 193 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 194 $a0.into(), 195 $a1.into(), 196 $a2.into(), 197 $a3.into(), 198 $a4.into(), 199 ) 200 }; 201 202 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => { 203 $crate::backend::arch::asm::syscall6( 204 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 205 $a0.into(), 206 $a1.into(), 207 $a2.into(), 208 $a3.into(), 209 $a4.into(), 210 $a5.into(), 211 ) 212 }; 213 214 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => { 215 $crate::backend::arch::asm::syscall7( 216 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 217 $a0.into(), 218 $a1.into(), 219 $a2.into(), 220 $a3.into(), 221 $a4.into(), 222 $a5.into(), 223 $a6.into(), 224 ) 225 }; 226 } 227 228 /// Like `syscall`, but adds the `readonly` attribute to the inline asm, which 229 /// indicates that the syscall does not mutate any memory. 230 macro_rules! syscall_readonly { 231 ($nr:ident) => { 232 $crate::backend::arch::choose::syscall0_readonly($crate::backend::reg::nr( 233 linux_raw_sys::general::$nr, 234 )) 235 }; 236 237 ($nr:ident, $a0:expr) => { 238 $crate::backend::arch::choose::syscall1_readonly( 239 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 240 $a0.into(), 241 ) 242 }; 243 244 ($nr:ident, $a0:expr, $a1:expr) => { 245 $crate::backend::arch::choose::syscall2_readonly( 246 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 247 $a0.into(), 248 $a1.into(), 249 ) 250 }; 251 252 ($nr:ident, $a0:expr, $a1:expr, $a2:expr) => { 253 $crate::backend::arch::choose::syscall3_readonly( 254 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 255 $a0.into(), 256 $a1.into(), 257 $a2.into(), 258 ) 259 }; 260 261 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr) => { 262 $crate::backend::arch::choose::syscall4_readonly( 263 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 264 $a0.into(), 265 $a1.into(), 266 $a2.into(), 267 $a3.into(), 268 ) 269 }; 270 271 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => { 272 $crate::backend::arch::choose::syscall5_readonly( 273 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 274 $a0.into(), 275 $a1.into(), 276 $a2.into(), 277 $a3.into(), 278 $a4.into(), 279 ) 280 }; 281 282 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr) => { 283 $crate::backend::arch::choose::syscall6_readonly( 284 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 285 $a0.into(), 286 $a1.into(), 287 $a2.into(), 288 $a3.into(), 289 $a4.into(), 290 $a5.into(), 291 ) 292 }; 293 294 ($nr:ident, $a0:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr, $a5:expr, $a6:expr) => { 295 $crate::backend::arch::choose::syscall7_readonly( 296 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 297 $a0.into(), 298 $a1.into(), 299 $a2.into(), 300 $a3.into(), 301 $a4.into(), 302 $a5.into(), 303 $a6.into(), 304 ) 305 }; 306 } 307 308 /// Like `syscall`, but indicates that the syscall does not return. 309 #[cfg(feature = "runtime")] 310 macro_rules! syscall_noreturn { 311 ($nr:ident, $a0:expr) => { 312 $crate::backend::arch::choose::syscall1_noreturn( 313 $crate::backend::reg::nr(linux_raw_sys::general::$nr), 314 $a0.into(), 315 ) 316 }; 317 } 318