Skip to content

Commit

Permalink
Fix the definition of sigevent on FreeBSD and Linux
Browse files Browse the repository at this point in the history
It was originally defined back before rust could represent C unions.  So
instead of defining the union field correctly, it simply defined that
union's most useful field.  Define it correctly now.

Remove traits that can't be safely implemented on a union: PartialEq,
Eq, and Hash.  Define Debug, but exclude the union field.
  • Loading branch information
asomers authored and tgross35 committed Nov 25, 2024
1 parent ca3a399 commit d65bb74
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 104 deletions.
17 changes: 14 additions & 3 deletions libc-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2080,6 +2080,9 @@ fn test_android(target: &str) {
("Elf32_Phdr", "p_type") => true,
("Elf64_Phdr", "p_type") => true,

// _sigev_un is an anonymous union
("sigevent", "_sigev_un") => true,

// this is actually a union on linux, so we can't represent it well and
// just insert some padding.
("siginfo_t", "_pad") => true,
Expand Down Expand Up @@ -2671,7 +2674,7 @@ fn test_freebsd(target: &str) {
cfg.volatile_item(|i| {
use ctest::VolatileItemKind::*;
match i {
// aio_buf is a volatile void** but since we cannot express that in
// aio_buf is a volatile void* but since we cannot express that in
// Rust types, we have to explicitly tell the checker about it here:
StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
_ => false,
Expand All @@ -2691,6 +2694,9 @@ fn test_freebsd(target: &str) {
// not available until FreeBSD 12, and is an anonymous union there.
("xucred", "cr_pid__c_anonymous_union") => true,

// Anonymous union
("sigevent", "_sigev_un") => true,

// m_owner field is a volatile __lwpid_t
("umutex", "m_owner") => true,
// c_has_waiters field is a volatile int32_t
Expand Down Expand Up @@ -2883,6 +2889,9 @@ fn test_emscripten(target: &str) {
});

cfg.skip_struct(move |ty| {
if ty.starts_with("__c_anonymous_") {
return true;
}
match ty {
// This is actually a union, not a struct
"sigval" => true,
Expand Down Expand Up @@ -2968,6 +2977,8 @@ fn test_emscripten(target: &str) {
});

cfg.skip_field(move |struct_, field| {
// _sigev_un is an anonymous union
(struct_ == "sigevent" && field == "_sigev_un") ||
// this is actually a union on linux, so we can't represent it well and
// just insert some padding.
(struct_ == "siginfo_t" && field == "_pad") ||
Expand Down Expand Up @@ -4359,8 +4370,8 @@ 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") ||
// _sigev_un is an anonymous union
(struct_ == "sigevent" && field == "_sigev_un") ||
// signalfd had SIGSYS fields added in Linux 4.18, but no libc release
// has them yet.
(struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
Expand Down
75 changes: 37 additions & 38 deletions src/unix/bsd/freebsdlike/freebsd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,20 +236,10 @@ impl ::Clone for devstat_select_mode {
}

s! {
pub struct aiocb {
pub aio_fildes: ::c_int,
pub aio_offset: ::off_t,
pub aio_buf: *mut ::c_void,
pub aio_nbytes: ::size_t,
__unused1: [::c_int; 2],
__unused2: *mut ::c_void,
pub aio_lio_opcode: ::c_int,
pub aio_reqprio: ::c_int,
// unused 3 through 5 are the __aiocb_private structure
__unused3: ::c_long,
__unused4: ::c_long,
__unused5: *mut ::c_void,
pub aio_sigevent: sigevent,
pub struct __c_anonymous_sigev_thread {
pub _function: Option<extern "C" fn(::sigval) -> *mut ::c_void>,
//pub _function: *mut ::c_void, // Actually a function pointer
pub _attribute: *mut ::pthread_attr_t,
}

pub struct jail {
Expand Down Expand Up @@ -1351,6 +1341,36 @@ s! {
}

s_no_extra_traits! {
#[cfg_attr(feature = "extra_traits", derive(Debug))]
pub struct __aiocb_private {
status: ::c_long,
error: ::c_long,
spare: *mut ::c_void,
}

#[cfg_attr(feature = "extra_traits", derive(Debug))]
pub struct aiocb {
pub aio_fildes: ::c_int,
pub aio_offset: ::off_t,
pub aio_buf: *mut ::c_void,
pub aio_nbytes: ::size_t,
__spare__: [::c_int; 2],
__spare2__: *mut ::c_void,
pub aio_lio_opcode: ::c_int,
pub aio_reqprio: ::c_int,
_aiocb_private: __aiocb_private,
pub aio_sigevent: sigevent,
}

// Can't correctly impl Debug for unions
#[allow(missing_debug_implementations)]
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],
}

pub struct utmpx {
pub ut_type: ::c_short,
pub ut_tv: ::timeval,
Expand Down Expand Up @@ -1398,12 +1418,7 @@ s_no_extra_traits! {
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 {
Expand Down Expand Up @@ -1819,33 +1834,17 @@ cfg_if! {
}
}

impl PartialEq for sigevent {
fn eq(&self, other: &sigevent) -> bool {
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
}
}
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)
// Skip _sigev_un, since we can't guarantee that it will be
// properly initialized.
.finish()
}
}
impl ::hash::Hash for sigevent {
fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
self.sigev_notify.hash(state);
self.sigev_signo.hash(state);
self.sigev_value.hash(state);
self.sigev_notify_thread_id.hash(state);
}
}

impl PartialEq for ptsstat {
fn eq(&self, other: &ptsstat) -> bool {
Expand Down
40 changes: 22 additions & 18 deletions src/unix/linux_like/linux/gnu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,6 @@ cfg_if! {
}

s! {
pub struct aiocb {
pub aio_fildes: ::c_int,
pub aio_lio_opcode: ::c_int,
pub aio_reqprio: ::c_int,
pub aio_buf: *mut ::c_void,
pub aio_nbytes: ::size_t,
pub aio_sigevent: ::sigevent,
__next_prio: *mut aiocb,
__abs_prio: ::c_int,
__policy: ::c_int,
__error_code: ::c_int,
__return_value: ::ssize_t,
pub aio_offset: off_t,
#[cfg(all(not(target_arch = "x86_64"), target_pointer_width = "32"))]
__unused1: [::c_char; 4],
__glibc_reserved: [::c_char; 32],
}

pub struct __exit_status {
pub e_termination: ::c_short,
pub e_exit: ::c_short,
Expand Down Expand Up @@ -510,6 +492,28 @@ impl siginfo_t {
}
}

s_no_extra_traits! {
#[cfg_attr(feature = "extra_traits", derive(Debug))]
pub struct aiocb {
pub aio_fildes: ::c_int,
pub aio_lio_opcode: ::c_int,
pub aio_reqprio: ::c_int,
pub aio_buf: *mut ::c_void,
pub aio_nbytes: ::size_t,
pub aio_sigevent: ::sigevent,
__next_prio: *mut aiocb,
__abs_prio: ::c_int,
__policy: ::c_int,
__error_code: ::c_int,
__return_value: ::ssize_t,
// FIXME(off64): visible fields depend on __USE_FILE_OFFSET64
pub aio_offset: off_t,
#[cfg(all(not(target_arch = "x86_64"), target_pointer_width = "32"))]
__pad: [::c_char; 4],
__glibc_reserved: [::c_char; 32],
}
}

// Internal, for casts to access union fields
#[repr(C)]
struct sifields_sigchld {
Expand Down
42 changes: 22 additions & 20 deletions src/unix/linux_like/linux/musl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,26 +117,6 @@ impl siginfo_t {
}

s! {
pub struct aiocb {
pub aio_fildes: ::c_int,
pub aio_lio_opcode: ::c_int,
pub aio_reqprio: ::c_int,
pub aio_buf: *mut ::c_void,
pub aio_nbytes: ::size_t,
pub aio_sigevent: ::sigevent,
__td: *mut ::c_void,
__lock: [::c_int; 2],
__err: ::c_int,
__ret: ::ssize_t,
pub aio_offset: off_t,
__next: *mut ::c_void,
__prev: *mut ::c_void,
#[cfg(target_pointer_width = "32")]
__dummy4: [::c_char; 24],
#[cfg(target_pointer_width = "64")]
__dummy4: [::c_char; 16],
}

pub struct sigaction {
pub sa_sigaction: ::sighandler_t,
pub sa_mask: ::sigset_t,
Expand Down Expand Up @@ -419,6 +399,28 @@ s! {
}

s_no_extra_traits! {
#[cfg_attr(feature = "extra_traits", derive(Debug))]
pub struct aiocb {
pub aio_fildes: ::c_int,
pub aio_lio_opcode: ::c_int,
pub aio_reqprio: ::c_int,
pub aio_buf: *mut ::c_void,
pub aio_nbytes: ::size_t,
pub aio_sigevent: ::sigevent,
__td: *mut ::c_void,
__lock: [::c_int; 2],
__err: ::c_int,
__ret: ::ssize_t,
pub aio_offset: off_t,
__next: *mut ::c_void,
__prev: *mut ::c_void,
// FIXME(ctest): length should be `32 - 2 * core::mem::size_of::<*const ()>()`
#[cfg(target_pointer_width = "32")]
__dummy4: [::c_char; 24],
#[cfg(target_pointer_width = "64")]
__dummy4: [::c_char; 16],
}

pub struct sysinfo {
pub uptime: ::c_ulong,
pub loads: [::c_ulong; 3],
Expand Down
51 changes: 26 additions & 25 deletions src/unix/linux_like/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ missing! {
}

s! {
pub struct __c_anonymous_sigev_thread {
pub _function: Option<extern "C" fn(::sigval) -> *mut ::c_void>,
pub _attribute: *mut ::pthread_attr_t,
}

pub struct in_addr {
pub s_addr: ::in_addr_t,
}
Expand Down Expand Up @@ -261,6 +266,14 @@ s_no_extra_traits! {
pub u64: u64,
}

// Can't correctly impl Debug for unions
#[allow(missing_debug_implementations)]
pub union __c_anonymous_sigev_un {
_pad: [::c_int; SIGEV_PAD_SIZE],
pub _tid: ::c_int,
pub _sigev_thread: __c_anonymous_sigev_thread,
}

pub struct sockaddr_un {
pub sun_family: sa_family_t,
pub sun_path: [::c_char; 108],
Expand Down Expand Up @@ -288,13 +301,7 @@ s_no_extra_traits! {
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,
}
}

Expand Down Expand Up @@ -441,33 +448,17 @@ cfg_if! {
}
}

impl PartialEq for sigevent {
fn eq(&self, other: &sigevent) -> bool {
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
}
}
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)
// Skip _sigev_un, since we can't guarantee that it will be
// properly initialized.
.finish()
}
}
impl ::hash::Hash for sigevent {
fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
self.sigev_value.hash(state);
self.sigev_signo.hash(state);
self.sigev_notify.hash(state);
self.sigev_notify_thread_id.hash(state);
}
}
}
}

Expand Down Expand Up @@ -582,6 +573,16 @@ pub const SIGPIPE: ::c_int = 13;
pub const SIGALRM: ::c_int = 14;
pub const SIGTERM: ::c_int = 15;

const SIGEV_MAX_SIZE: usize = 64;
cfg_if! {
if #[cfg(target_pointer_width = "64")] {
const __ARCH_SIGEV_PREAMBLE_SIZE: usize = 4 * 2 + 8;
} else {
const __ARCH_SIGEV_PREAMBLE_SIZE: usize = 4 * 2 + 4;
}
}
const SIGEV_PAD_SIZE: usize = (SIGEV_MAX_SIZE - __ARCH_SIGEV_PREAMBLE_SIZE) / 4;

pub const PROT_NONE: ::c_int = 0;
pub const PROT_READ: ::c_int = 1;
pub const PROT_WRITE: ::c_int = 2;
Expand Down

0 comments on commit d65bb74

Please sign in to comment.