]> git.lizzy.rs Git - rust.git/blob - library/core/src/ffi.rs
Rollup merge of #82372 - RalfJung:unsafe-cell, r=KodrAus
[rust.git] / library / core / src / ffi.rs
1 #![stable(feature = "", since = "1.30.0")]
2 #![allow(non_camel_case_types)]
3
4 //! Utilities related to foreign function interface (FFI) bindings.
5
6 use crate::fmt;
7 use crate::marker::PhantomData;
8 use crate::ops::{Deref, DerefMut};
9
10 /// Equivalent to C's `void` type when used as a [pointer].
11 ///
12 /// In essence, `*const c_void` is equivalent to C's `const void*`
13 /// and `*mut c_void` is equivalent to C's `void*`. That said, this is
14 /// *not* the same as C's `void` return type, which is Rust's `()` type.
15 ///
16 /// To model pointers to opaque types in FFI, until `extern type` is
17 /// stabilized, it is recommended to use a newtype wrapper around an empty
18 /// byte array. See the [Nomicon] for details.
19 ///
20 /// One could use `std::os::raw::c_void` if they want to support old Rust
21 /// compiler down to 1.1.0. After Rust 1.30.0, it was re-exported by
22 /// this definition. For more information, please read [RFC 2521].
23 ///
24 /// [pointer]: ../../std/primitive.pointer.html
25 /// [Nomicon]: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs
26 /// [RFC 2521]: https://github.com/rust-lang/rfcs/blob/master/text/2521-c_void-reunification.md
27 // N.B., for LLVM to recognize the void pointer type and by extension
28 //     functions like malloc(), we need to have it represented as i8* in
29 //     LLVM bitcode. The enum used here ensures this and prevents misuse
30 //     of the "raw" type by only having private variants. We need two
31 //     variants, because the compiler complains about the repr attribute
32 //     otherwise and we need at least one variant as otherwise the enum
33 //     would be uninhabited and at least dereferencing such pointers would
34 //     be UB.
35 #[repr(u8)]
36 #[stable(feature = "core_c_void", since = "1.30.0")]
37 pub enum c_void {
38     #[unstable(
39         feature = "c_void_variant",
40         reason = "temporary implementation detail",
41         issue = "none"
42     )]
43     #[doc(hidden)]
44     __variant1,
45     #[unstable(
46         feature = "c_void_variant",
47         reason = "temporary implementation detail",
48         issue = "none"
49     )]
50     #[doc(hidden)]
51     __variant2,
52 }
53
54 #[stable(feature = "std_debug", since = "1.16.0")]
55 impl fmt::Debug for c_void {
56     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57         f.pad("c_void")
58     }
59 }
60
61 /// Basic implementation of a `va_list`.
62 // The name is WIP, using `VaListImpl` for now.
63 #[cfg(any(
64     all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
65     all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
66     target_arch = "wasm32",
67     target_arch = "asmjs",
68     windows
69 ))]
70 #[repr(transparent)]
71 #[unstable(
72     feature = "c_variadic",
73     reason = "the `c_variadic` feature has not been properly tested on \
74               all supported platforms",
75     issue = "44930"
76 )]
77 #[lang = "va_list"]
78 pub struct VaListImpl<'f> {
79     ptr: *mut c_void,
80
81     // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to
82     // the region of the function it's defined in
83     _marker: PhantomData<&'f mut &'f c_void>,
84 }
85
86 #[cfg(any(
87     all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
88     all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
89     target_arch = "wasm32",
90     target_arch = "asmjs",
91     windows
92 ))]
93 #[unstable(
94     feature = "c_variadic",
95     reason = "the `c_variadic` feature has not been properly tested on \
96               all supported platforms",
97     issue = "44930"
98 )]
99 impl<'f> fmt::Debug for VaListImpl<'f> {
100     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101         write!(f, "va_list* {:p}", self.ptr)
102     }
103 }
104
105 /// AArch64 ABI implementation of a `va_list`. See the
106 /// [AArch64 Procedure Call Standard] for more details.
107 ///
108 /// [AArch64 Procedure Call Standard]:
109 /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
110 #[cfg(all(
111     target_arch = "aarch64",
112     not(any(target_os = "macos", target_os = "ios")),
113     not(windows)
114 ))]
115 #[repr(C)]
116 #[derive(Debug)]
117 #[unstable(
118     feature = "c_variadic",
119     reason = "the `c_variadic` feature has not been properly tested on \
120               all supported platforms",
121     issue = "44930"
122 )]
123 #[lang = "va_list"]
124 pub struct VaListImpl<'f> {
125     stack: *mut c_void,
126     gr_top: *mut c_void,
127     vr_top: *mut c_void,
128     gr_offs: i32,
129     vr_offs: i32,
130     _marker: PhantomData<&'f mut &'f c_void>,
131 }
132
133 /// PowerPC ABI implementation of a `va_list`.
134 #[cfg(all(target_arch = "powerpc", not(windows)))]
135 #[repr(C)]
136 #[derive(Debug)]
137 #[unstable(
138     feature = "c_variadic",
139     reason = "the `c_variadic` feature has not been properly tested on \
140               all supported platforms",
141     issue = "44930"
142 )]
143 #[lang = "va_list"]
144 pub struct VaListImpl<'f> {
145     gpr: u8,
146     fpr: u8,
147     reserved: u16,
148     overflow_arg_area: *mut c_void,
149     reg_save_area: *mut c_void,
150     _marker: PhantomData<&'f mut &'f c_void>,
151 }
152
153 /// x86_64 ABI implementation of a `va_list`.
154 #[cfg(all(target_arch = "x86_64", not(windows)))]
155 #[repr(C)]
156 #[derive(Debug)]
157 #[unstable(
158     feature = "c_variadic",
159     reason = "the `c_variadic` feature has not been properly tested on \
160               all supported platforms",
161     issue = "44930"
162 )]
163 #[lang = "va_list"]
164 pub struct VaListImpl<'f> {
165     gp_offset: i32,
166     fp_offset: i32,
167     overflow_arg_area: *mut c_void,
168     reg_save_area: *mut c_void,
169     _marker: PhantomData<&'f mut &'f c_void>,
170 }
171
172 /// A wrapper for a `va_list`
173 #[repr(transparent)]
174 #[derive(Debug)]
175 #[unstable(
176     feature = "c_variadic",
177     reason = "the `c_variadic` feature has not been properly tested on \
178               all supported platforms",
179     issue = "44930"
180 )]
181 pub struct VaList<'a, 'f: 'a> {
182     #[cfg(any(
183         all(
184             not(target_arch = "aarch64"),
185             not(target_arch = "powerpc"),
186             not(target_arch = "x86_64")
187         ),
188         all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
189         target_arch = "wasm32",
190         target_arch = "asmjs",
191         windows
192     ))]
193     inner: VaListImpl<'f>,
194
195     #[cfg(all(
196         any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
197         any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))),
198         not(target_arch = "wasm32"),
199         not(target_arch = "asmjs"),
200         not(windows)
201     ))]
202     inner: &'a mut VaListImpl<'f>,
203
204     _marker: PhantomData<&'a mut VaListImpl<'f>>,
205 }
206
207 #[cfg(any(
208     all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
209     all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
210     target_arch = "wasm32",
211     target_arch = "asmjs",
212     windows
213 ))]
214 #[unstable(
215     feature = "c_variadic",
216     reason = "the `c_variadic` feature has not been properly tested on \
217               all supported platforms",
218     issue = "44930"
219 )]
220 impl<'f> VaListImpl<'f> {
221     /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
222     #[inline]
223     pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
224         VaList { inner: VaListImpl { ..*self }, _marker: PhantomData }
225     }
226 }
227
228 #[cfg(all(
229     any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
230     any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))),
231     not(target_arch = "wasm32"),
232     not(target_arch = "asmjs"),
233     not(windows)
234 ))]
235 #[unstable(
236     feature = "c_variadic",
237     reason = "the `c_variadic` feature has not been properly tested on \
238               all supported platforms",
239     issue = "44930"
240 )]
241 impl<'f> VaListImpl<'f> {
242     /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`.
243     #[inline]
244     pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> {
245         VaList { inner: self, _marker: PhantomData }
246     }
247 }
248
249 #[unstable(
250     feature = "c_variadic",
251     reason = "the `c_variadic` feature has not been properly tested on \
252               all supported platforms",
253     issue = "44930"
254 )]
255 impl<'a, 'f: 'a> Deref for VaList<'a, 'f> {
256     type Target = VaListImpl<'f>;
257
258     #[inline]
259     fn deref(&self) -> &VaListImpl<'f> {
260         &self.inner
261     }
262 }
263
264 #[unstable(
265     feature = "c_variadic",
266     reason = "the `c_variadic` feature has not been properly tested on \
267               all supported platforms",
268     issue = "44930"
269 )]
270 impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
271     #[inline]
272     fn deref_mut(&mut self) -> &mut VaListImpl<'f> {
273         &mut self.inner
274     }
275 }
276
277 // The VaArgSafe trait needs to be used in public interfaces, however, the trait
278 // itself must not be allowed to be used outside this module. Allowing users to
279 // implement the trait for a new type (thereby allowing the va_arg intrinsic to
280 // be used on a new type) is likely to cause undefined behavior.
281 //
282 // FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface
283 // but also ensure it cannot be used elsewhere, the trait needs to be public
284 // within a private module. Once RFC 2145 has been implemented look into
285 // improving this.
286 mod sealed_trait {
287     /// Trait which permits the allowed types to be used with [super::VaListImpl::arg].
288     #[unstable(
289         feature = "c_variadic",
290         reason = "the `c_variadic` feature has not been properly tested on \
291                   all supported platforms",
292         issue = "44930"
293     )]
294     pub trait VaArgSafe {}
295 }
296
297 macro_rules! impl_va_arg_safe {
298     ($($t:ty),+) => {
299         $(
300             #[unstable(feature = "c_variadic",
301                        reason = "the `c_variadic` feature has not been properly tested on \
302                                  all supported platforms",
303                        issue = "44930")]
304             impl sealed_trait::VaArgSafe for $t {}
305         )+
306     }
307 }
308
309 impl_va_arg_safe! {i8, i16, i32, i64, usize}
310 impl_va_arg_safe! {u8, u16, u32, u64, isize}
311 impl_va_arg_safe! {f64}
312
313 #[unstable(
314     feature = "c_variadic",
315     reason = "the `c_variadic` feature has not been properly tested on \
316               all supported platforms",
317     issue = "44930"
318 )]
319 impl<T> sealed_trait::VaArgSafe for *mut T {}
320 #[unstable(
321     feature = "c_variadic",
322     reason = "the `c_variadic` feature has not been properly tested on \
323               all supported platforms",
324     issue = "44930"
325 )]
326 impl<T> sealed_trait::VaArgSafe for *const T {}
327
328 #[unstable(
329     feature = "c_variadic",
330     reason = "the `c_variadic` feature has not been properly tested on \
331               all supported platforms",
332     issue = "44930"
333 )]
334 impl<'f> VaListImpl<'f> {
335     /// Advance to the next arg.
336     #[inline]
337     pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
338         // SAFETY: the caller must uphold the safety contract for `va_arg`.
339         unsafe { va_arg(self) }
340     }
341
342     /// Copies the `va_list` at the current location.
343     pub unsafe fn with_copy<F, R>(&self, f: F) -> R
344     where
345         F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R,
346     {
347         let mut ap = self.clone();
348         let ret = f(ap.as_va_list());
349         // SAFETY: the caller must uphold the safety contract for `va_end`.
350         unsafe {
351             va_end(&mut ap);
352         }
353         ret
354     }
355 }
356
357 #[unstable(
358     feature = "c_variadic",
359     reason = "the `c_variadic` feature has not been properly tested on \
360               all supported platforms",
361     issue = "44930"
362 )]
363 impl<'f> Clone for VaListImpl<'f> {
364     #[inline]
365     fn clone(&self) -> Self {
366         let mut dest = crate::mem::MaybeUninit::uninit();
367         // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal
368         unsafe {
369             va_copy(dest.as_mut_ptr(), self);
370             dest.assume_init()
371         }
372     }
373 }
374
375 #[unstable(
376     feature = "c_variadic",
377     reason = "the `c_variadic` feature has not been properly tested on \
378               all supported platforms",
379     issue = "44930"
380 )]
381 impl<'f> Drop for VaListImpl<'f> {
382     fn drop(&mut self) {
383         // FIXME: this should call `va_end`, but there's no clean way to
384         // guarantee that `drop` always gets inlined into its caller,
385         // so the `va_end` would get directly called from the same function as
386         // the corresponding `va_copy`. `man va_end` states that C requires this,
387         // and LLVM basically follows the C semantics, so we need to make sure
388         // that `va_end` is always called from the same function as `va_copy`.
389         // For more details, see https://github.com/rust-lang/rust/pull/59625
390         // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic.
391         //
392         // This works for now, since `va_end` is a no-op on all current LLVM targets.
393     }
394 }
395
396 extern "rust-intrinsic" {
397     /// Destroy the arglist `ap` after initialization with `va_start` or
398     /// `va_copy`.
399     fn va_end(ap: &mut VaListImpl<'_>);
400
401     /// Copies the current location of arglist `src` to the arglist `dst`.
402     fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
403
404     /// Loads an argument of type `T` from the `va_list` `ap` and increment the
405     /// argument `ap` points to.
406     fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
407 }