diff --git a/libc-test/build.rs b/libc-test/build.rs index a3f940a3e823e..becc9d4c19f80 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -1409,7 +1409,7 @@ fn test_dragonflybsd(target: &str) { // just insert some padding. (struct_ == "siginfo_t" && field == "_pad") || // sigev_notify_thread_id is actually part of a sigev_un union - (struct_ == "sigevent" && field == "sigev_notify_thread_id") + (struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") }); cfg.generate("../src/lib.rs", "main.rs"); @@ -1645,6 +1645,8 @@ fn test_android(target: &str) { // sigval is a struct in Rust, but a union in C: "sigval" => format!("union sigval"), + "sigevent_0_2_126" => "struct sigevent".to_string(), + // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), @@ -1802,8 +1804,10 @@ fn test_android(target: &str) { // FIXME: `sa_sigaction` has type `sighandler_t` but that type is // incorrect, see: https://github.com/rust-lang/libc/issues/1359 (struct_ == "sigaction" && field == "sa_sigaction") || - // sigev_notify_thread_id is actually part of a sigev_un union - (struct_ == "sigevent" && field == "sigev_notify_thread_id") || + // union field + (struct_ == "sigevent" && field == "_sigev_un") || + // union field on the backwards-compat struct definition + (struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") || // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet. (struct_ == "signalfd_siginfo" && (field == "ssi_syscall" || field == "ssi_call_addr" || @@ -1980,6 +1984,8 @@ fn test_freebsd(target: &str) { // FIXME: https://github.com/rust-lang/libc/issues/1273 "sighandler_t" => "sig_t".to_string(), + "sigevent_0_2_126" => "struct sigevent".to_string(), + t if is_union => format!("union {}", t), t if t.ends_with("_t") => t.to_string(), @@ -2270,6 +2276,7 @@ fn test_freebsd(target: &str) { ("if_data", "__ifi_lastchange") => true, ("ifreq", "ifr_ifru") => true, ("ifconf", "ifc_ifcu") => true, + ("sigevent", "_sigev_un") => true, // anonymous struct ("devstat", "dev_links") => true, @@ -2409,6 +2416,8 @@ fn test_emscripten(target: &str) { t if t.ends_with("_t") => t.to_string(), + "sigevent_0_2_126" => "struct sigevent".to_string(), + // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), @@ -2512,8 +2521,10 @@ fn test_emscripten(target: &str) { // musl seems to define this as an *anonymous* bitfield // FIXME: is this necessary? (struct_ == "statvfs" && field == "__f_unused") || - // sigev_notify_thread_id is actually part of a sigev_un union - (struct_ == "sigevent" && field == "sigev_notify_thread_id") || + // union field + (struct_ == "sigevent" && field == "_sigev_un") || + // union field on the backwards-compat struct definition + (struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") || // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet. (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" || field == "_pad2" || @@ -2863,6 +2874,8 @@ fn test_linux(target: &str) { // In MUSL `flock64` is a typedef to `flock`. "flock64" if musl => format!("struct {}", ty), + "sigevent_0_2_126" => "struct sigevent".to_string(), + // put `struct` in front of all structs:. t if is_struct => format!("struct {}", t), @@ -3286,6 +3299,7 @@ fn test_linux(target: &str) { (struct_ == "utmpx" && field == "ut_tv") || // sigval is actually a union, but we pretend it's a struct (struct_ == "sigevent" && field == "sigev_value") || + (struct_ == "sigevent_0_2_126" && field == "sigev_value") || // this one is an anonymous union (struct_ == "ff_effect" && field == "u") || // `__exit_status` type is a patch which is absent in musl @@ -3312,8 +3326,10 @@ fn test_linux(target: &str) { (musl && struct_ == "glob_t" && field == "gl_flags") || // musl seems to define this as an *anonymous* bitfield (musl && struct_ == "statvfs" && field == "__f_unused") || - // sigev_notify_thread_id is actually part of a sigev_un union - (struct_ == "sigevent" && field == "sigev_notify_thread_id") || + // union field + (struct_ == "sigevent" && field == "_sigev_un") || + // union field on the backwards-compat struct definition + (struct_ == "sigevent_0_2_126" && field == "sigev_notify_thread_id") || // signalfd had SIGSYS fields added in Linux 4.18, but no libc release // has them yet. (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" || @@ -3810,6 +3826,7 @@ fn test_haiku(target: &str) { ("sem_t", "named_sem_id") => true, ("sigaction", "sa_sigaction") => true, ("sigevent", "sigev_value") => true, + ("sigevent_0_2_126", "sigev_value") => true, ("fpu_state", "_fpreg") => true, // these fields have a simplified data definition in libc ("fpu_state", "_xmm") => true, diff --git a/src/unix/bsd/freebsdlike/freebsd/mod.rs b/src/unix/bsd/freebsdlike/freebsd/mod.rs index a44af09eb6e97..55e39149f9e97 100644 --- a/src/unix/bsd/freebsdlike/freebsd/mod.rs +++ b/src/unix/bsd/freebsdlike/freebsd/mod.rs @@ -871,6 +871,12 @@ s! { pub profhz: ::c_int, } + #[cfg(libc_union)] + pub struct __c_anonymous_sigev_thread { + pub _function: *mut ::c_void, // Actually a function pointer + pub _attribute: *mut ::pthread_attr_t, + } + pub struct __c_anonymous_stailq_entry_devstat { pub stqe_next: *mut devstat, } @@ -990,6 +996,21 @@ s! { pub _flags: u32, pub _clockid: u32, } + + // When sigevent was first added to libc, Rust still didn't support unions. + // So the definition only included one of the union's member. This + // structure exists for backwards-compatibility with consumers that still + // try to access that one member. + #[doc(hidden)] + pub struct sigevent_0_2_126 { + pub sigev_notify: ::c_int, + pub sigev_signo: ::c_int, + pub sigev_value: ::sigval, + pub sigev_notify_thread_id: ::lwpid_t, + #[cfg(target_pointer_width = "64")] + __unused1: ::c_int, + __unused2: [::c_long; 7] + } } s_no_extra_traits! { @@ -1040,16 +1061,22 @@ s_no_extra_traits! { __reserved: [::c_long; 4] } + // Can't correctly impl Debug for unions + #[allow(missing_debug_implementations)] + #[cfg(libc_union)] + pub union __c_anonymous_sigev_un { + pub _threadid: ::__lwpid_t, + pub _sigev_thread: __c_anonymous_sigev_thread, + pub _kevent_flags: ::c_ushort, + __spare__: [::c_long; 8], + } + + #[cfg(libc_union)] pub struct sigevent { pub sigev_notify: ::c_int, pub sigev_signo: ::c_int, pub sigev_value: ::sigval, - //The rest of the structure is actually a union. We expose only - //sigev_notify_thread_id because it's the most useful union member. - pub sigev_notify_thread_id: ::lwpid_t, - #[cfg(target_pointer_width = "64")] - __unused1: ::c_int, - __unused2: [::c_long; 7] + pub _sigev_un: __c_anonymous_sigev_un, } pub struct ptsstat { @@ -1202,6 +1229,24 @@ s_no_extra_traits! { } } +impl core::ops::Deref for sigevent { + type Target = sigevent_0_2_126; + + fn deref(&self) -> &Self::Target { + unsafe { + &*(self as *const Self as *const sigevent_0_2_126) + } + } +} + +impl core::ops::DerefMut for sigevent { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + &mut *(self as *mut Self as *mut sigevent_0_2_126) + } + } +} + cfg_if! { if #[cfg(feature = "extra_traits")] { impl PartialEq for utmpx { @@ -1396,20 +1441,46 @@ cfg_if! { self.sigev_notify == other.sigev_notify && self.sigev_signo == other.sigev_signo && self.sigev_value == other.sigev_value - && self.sigev_notify_thread_id - == other.sigev_notify_thread_id + // sigev_notify indicates which union fields are valid + && match self.sigev_notify { + ::SIGEV_NONE => true, + ::SIGEV_SIGNAL => true, + ::SIGEV_THREAD => unsafe { + self._sigev_un._sigev_thread + == other._sigev_un._sigev_thread + }, + ::SIGEV_KEVENT => unsafe { + self._sigev_un._kevent_flags + == other._sigev_un._kevent_flags + }, + ::SIGEV_THREAD_ID => unsafe { + self._sigev_un._threadid + == other._sigev_un._threadid + }, + _ => false + } } } impl Eq for sigevent {} impl ::fmt::Debug for sigevent { fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { - f.debug_struct("sigevent") - .field("sigev_notify", &self.sigev_notify) - .field("sigev_signo", &self.sigev_signo) - .field("sigev_value", &self.sigev_value) - .field("sigev_notify_thread_id", - &self.sigev_notify_thread_id) - .finish() + let mut ds = f.debug_struct("sigevent"); + ds.field("sigev_notify", &self.sigev_notify); + ds.field("sigev_signo", &self.sigev_signo); + ds.field("sigev_value", &self.sigev_value); + // The sigev_notify field indicates which union fields are valid + unsafe { + match self.sigev_notify { + ::SIGEV_THREAD => ds.field("_sigev_thread", + &self._sigev_un._sigev_thread), + ::SIGEV_KEVENT => ds.field("_kevent_flags", + &self._sigev_un._kevent_flags), + ::SIGEV_THREAD_ID => ds.field("_threadid", + &self._sigev_un._threadid), + _ => &mut ds + } + }; + ds.finish() } } impl ::hash::Hash for sigevent { @@ -1417,7 +1488,15 @@ cfg_if! { self.sigev_notify.hash(state); self.sigev_signo.hash(state); self.sigev_value.hash(state); - self.sigev_notify_thread_id.hash(state); + // The sigev_notify field indicates which union fields are valid + unsafe { + match self.sigev_notify { + ::SIGEV_THREAD => self._sigev_un._sigev_thread.hash(state), + ::SIGEV_KEVENT => self._sigev_un._kevent_flags.hash(state), + ::SIGEV_THREAD_ID => self._sigev_un._threadid.hash(state), + _ => () + }; + } } } diff --git a/src/unix/linux_like/mod.rs b/src/unix/linux_like/mod.rs index e74f1811cc2c2..6723949fba802 100644 --- a/src/unix/linux_like/mod.rs +++ b/src/unix/linux_like/mod.rs @@ -16,6 +16,12 @@ impl ::Clone for timezone { } s! { + #[cfg(libc_union)] + pub struct __c_anonymous_sigev_thread { + pub _function: *mut ::c_void, // Actually a function pointer + pub _attribute: *mut ::pthread_attr_t, + } + pub struct in_addr { pub s_addr: ::in_addr_t, } @@ -208,6 +214,22 @@ s! { pub msg_hdr: ::msghdr, pub msg_len: ::c_uint, } + + // When sigevent was first added to libc, Rust still didn't support unions. + // So the definition only included one of the union's member. This + // structure exists for backwards-compatibility with consumers that still + // try to access that one member. + #[doc(hidden)] + pub struct sigevent_0_2_126 { + pub sigev_value: ::sigval, + pub sigev_signo: ::c_int, + pub sigev_notify: ::c_int, + pub sigev_notify_thread_id: ::c_int, + #[cfg(target_pointer_width = "64")] + __unused1: [::c_int; 11], + #[cfg(target_pointer_width = "32")] + __unused1: [::c_int; 12] + } } s_no_extra_traits! { @@ -247,17 +269,42 @@ s_no_extra_traits! { pub domainname: [::c_char; 65] } + // Can't correctly impl Debug for unions + #[allow(missing_debug_implementations)] + #[cfg(libc_union)] + pub union __c_anonymous_sigev_un { + #[cfg(target_pointer_width = "64")] + _pad: [::c_int; (64 - 2 * 4 - 8) / 4], + #[cfg(target_pointer_width = "32")] + _pad: [::c_int; (64 - 2 * 4 - 4) / 4], + pub _tid: ::c_int, + pub _sigev_thread: __c_anonymous_sigev_thread, + } + + #[cfg(libc_union)] pub struct sigevent { pub sigev_value: ::sigval, pub sigev_signo: ::c_int, pub sigev_notify: ::c_int, - // Actually a union. We only expose sigev_notify_thread_id because it's - // the most useful member - pub sigev_notify_thread_id: ::c_int, - #[cfg(target_pointer_width = "64")] - __unused1: [::c_int; 11], - #[cfg(target_pointer_width = "32")] - __unused1: [::c_int; 12] + pub _sigev_un: __c_anonymous_sigev_un, + } +} + +impl core::ops::Deref for sigevent { + type Target = sigevent_0_2_126; + + fn deref(&self) -> &Self::Target { + unsafe { + &*(self as *const Self as *const sigevent_0_2_126) + } + } +} + +impl core::ops::DerefMut for sigevent { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + &mut *(self as *mut Self as *mut sigevent_0_2_126) + } } } @@ -410,20 +457,49 @@ cfg_if! { self.sigev_value == other.sigev_value && self.sigev_signo == other.sigev_signo && self.sigev_notify == other.sigev_notify - && self.sigev_notify_thread_id - == other.sigev_notify_thread_id + // sigev_notify indicates which union fields are valid + && match self.sigev_notify { + ::SIGEV_NONE => true, + ::SIGEV_SIGNAL => true, + ::SIGEV_THREAD => unsafe { + self._sigev_un._sigev_thread + == other._sigev_un._sigev_thread + }, + #[cfg(any( + target_os = "android", + target_env = "gnu", + target_env = "uclibc", + ))] + ::SIGEV_THREAD_ID => unsafe { + self._sigev_un._tid == other._sigev_un._tid + }, + _ => false + } } } impl Eq for sigevent {} impl ::fmt::Debug for sigevent { fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { - f.debug_struct("sigevent") - .field("sigev_value", &self.sigev_value) - .field("sigev_signo", &self.sigev_signo) - .field("sigev_notify", &self.sigev_notify) - .field("sigev_notify_thread_id", - &self.sigev_notify_thread_id) - .finish() + let mut ds = f.debug_struct("sigevent"); + ds.field("sigev_value", &self.sigev_value); + ds.field("sigev_signo", &self.sigev_signo); + ds.field("sigev_notify", &self.sigev_notify); + // The sigev_notify field indicates which union fields are valid + unsafe { + match self.sigev_notify { + ::SIGEV_THREAD => ds.field("_sigev_thread", + &self._sigev_un._sigev_thread), + #[cfg(any( + target_os = "android", + target_env = "gnu", + target_env = "uclibc", + ))] + ::SIGEV_THREAD_ID => ds.field("_tid", + &self._sigev_un._tid), + _ => &mut ds + } + }; + ds.finish() } } impl ::hash::Hash for sigevent { @@ -431,7 +507,19 @@ cfg_if! { self.sigev_value.hash(state); self.sigev_signo.hash(state); self.sigev_notify.hash(state); - self.sigev_notify_thread_id.hash(state); + // The sigev_notify field indicates which union fields are valid + unsafe { + match self.sigev_notify { + ::SIGEV_THREAD => self._sigev_un._sigev_thread.hash(state), + #[cfg(any( + target_os = "android", + target_env = "gnu", + target_env = "uclibc", + ))] + ::SIGEV_THREAD_ID => self._sigev_un._tid.hash(state), + _ => () + }; + } } } }