diff --git a/libvlc-sys/bindings.rs b/libvlc-sys/bindings.rs index baef517..08ec9d6 100644 --- a/libvlc-sys/bindings.rs +++ b/libvlc-sys/bindings.rs @@ -1,145 +1,7 @@ /* automatically generated by rust-bindgen 0.72.1 */ -#[repr(C)] -#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct __BindgenBitfieldUnit { - storage: Storage, -} -impl __BindgenBitfieldUnit { - #[inline] - pub const fn new(storage: Storage) -> Self { - Self { storage } - } -} -impl __BindgenBitfieldUnit -where - Storage: AsRef<[u8]> + AsMut<[u8]>, -{ - #[inline] - fn extract_bit(byte: u8, index: usize) -> bool { - let bit_index = if cfg!(target_endian = "big") { - 7 - (index % 8) - } else { - index % 8 - }; - let mask = 1 << bit_index; - byte & mask == mask - } - #[inline] - pub fn get_bit(&self, index: usize) -> bool { - debug_assert!(index / 8 < self.storage.as_ref().len()); - let byte_index = index / 8; - let byte = self.storage.as_ref()[byte_index]; - Self::extract_bit(byte, index) - } - #[inline] - pub unsafe fn raw_get_bit(this: *const Self, index: usize) -> bool { - debug_assert!(index / 8 < core::mem::size_of::()); - let byte_index = index / 8; - let byte = unsafe { - *(core::ptr::addr_of!((*this).storage) as *const u8).offset(byte_index as isize) - }; - Self::extract_bit(byte, index) - } - #[inline] - fn change_bit(byte: u8, index: usize, val: bool) -> u8 { - let bit_index = if cfg!(target_endian = "big") { - 7 - (index % 8) - } else { - index % 8 - }; - let mask = 1 << bit_index; - if val { - byte | mask - } else { - byte & !mask - } - } - #[inline] - pub fn set_bit(&mut self, index: usize, val: bool) { - debug_assert!(index / 8 < self.storage.as_ref().len()); - let byte_index = index / 8; - let byte = &mut self.storage.as_mut()[byte_index]; - *byte = Self::change_bit(*byte, index, val); - } - #[inline] - pub unsafe fn raw_set_bit(this: *mut Self, index: usize, val: bool) { - debug_assert!(index / 8 < core::mem::size_of::()); - let byte_index = index / 8; - let byte = unsafe { - (core::ptr::addr_of_mut!((*this).storage) as *mut u8).offset(byte_index as isize) - }; - unsafe { *byte = Self::change_bit(*byte, index, val) }; - } - #[inline] - pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); - let mut val = 0; - for i in 0..(bit_width as usize) { - if self.get_bit(i + bit_offset) { - let index = if cfg!(target_endian = "big") { - bit_width as usize - 1 - i - } else { - i - }; - val |= 1 << index; - } - } - val - } - #[inline] - pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!((bit_offset + (bit_width as usize)) / 8 <= core::mem::size_of::()); - let mut val = 0; - for i in 0..(bit_width as usize) { - if unsafe { Self::raw_get_bit(this, i + bit_offset) } { - let index = if cfg!(target_endian = "big") { - bit_width as usize - 1 - i - } else { - i - }; - val |= 1 << index; - } - } - val - } - #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); - for i in 0..(bit_width as usize) { - let mask = 1 << i; - let val_bit_is_set = val & mask == mask; - let index = if cfg!(target_endian = "big") { - bit_width as usize - 1 - i - } else { - i - }; - self.set_bit(index + bit_offset, val_bit_is_set); - } - } - #[inline] - pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < core::mem::size_of::()); - debug_assert!((bit_offset + (bit_width as usize)) / 8 <= core::mem::size_of::()); - for i in 0..(bit_width as usize) { - let mask = 1 << i; - let val_bit_is_set = val & mask == mask; - let index = if cfg!(target_endian = "big") { - bit_width as usize - 1 - i - } else { - i - }; - unsafe { Self::raw_set_bit(this, index + bit_offset, val_bit_is_set) }; - } - } -} +pub use crate::valist::VaList; + pub const VLC_VLC_H: u32 = 1; pub const VLC_LIBVLC_H: u32 = 1; pub const VLC_LIBVLC_RENDERER_DISCOVERER_H: u32 = 1; @@ -155,8 +17,6 @@ pub const LIBVLC_EVENTS_H: u32 = 1; pub const LIBVLC_DIALOG_H: u32 = 1; pub const LIBVLC_VLM_H: u32 = 1; pub const LIBVLC_DEPRECATED_H: u32 = 1; -pub type __gnuc_va_list = __builtin_va_list; -pub type __uint64_t = libc::c_ulong; pub type __off_t = libc::c_long; pub type __off64_t = libc::c_long; pub type FILE = _IO_FILE; @@ -194,9 +54,7 @@ pub struct _IO_FILE { pub _markers: *mut _IO_marker, pub _chain: *mut _IO_FILE, pub _fileno: libc::c_int, - pub _bitfield_align_1: [u32; 0], - pub _bitfield_1: __BindgenBitfieldUnit<[u8; 3usize]>, - pub _short_backupbuf: [libc::c_char; 1usize], + pub _flags2: libc::c_int, pub _old_offset: __off_t, pub _cur_column: libc::c_ushort, pub _vtable_offset: libc::c_schar, @@ -207,11 +65,9 @@ pub struct _IO_FILE { pub _wide_data: *mut _IO_wide_data, pub _freeres_list: *mut _IO_FILE, pub _freeres_buf: *mut libc::c_void, - pub _prevchain: *mut *mut _IO_FILE, + pub __pad5: usize, pub _mode: libc::c_int, - pub _unused3: libc::c_int, - pub _total_written: __uint64_t, - pub _unused2: [libc::c_char; 8usize], + pub _unused2: [libc::c_char; 20usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { @@ -243,8 +99,7 @@ const _: () = { ["Offset of field: _IO_FILE::_markers"][::core::mem::offset_of!(_IO_FILE, _markers) - 96usize]; ["Offset of field: _IO_FILE::_chain"][::core::mem::offset_of!(_IO_FILE, _chain) - 104usize]; ["Offset of field: _IO_FILE::_fileno"][::core::mem::offset_of!(_IO_FILE, _fileno) - 112usize]; - ["Offset of field: _IO_FILE::_short_backupbuf"] - [::core::mem::offset_of!(_IO_FILE, _short_backupbuf) - 119usize]; + ["Offset of field: _IO_FILE::_flags2"][::core::mem::offset_of!(_IO_FILE, _flags2) - 116usize]; ["Offset of field: _IO_FILE::_old_offset"] [::core::mem::offset_of!(_IO_FILE, _old_offset) - 120usize]; ["Offset of field: _IO_FILE::_cur_column"] @@ -262,65 +117,16 @@ const _: () = { [::core::mem::offset_of!(_IO_FILE, _freeres_list) - 168usize]; ["Offset of field: _IO_FILE::_freeres_buf"] [::core::mem::offset_of!(_IO_FILE, _freeres_buf) - 176usize]; - ["Offset of field: _IO_FILE::_prevchain"] - [::core::mem::offset_of!(_IO_FILE, _prevchain) - 184usize]; + ["Offset of field: _IO_FILE::__pad5"][::core::mem::offset_of!(_IO_FILE, __pad5) - 184usize]; ["Offset of field: _IO_FILE::_mode"][::core::mem::offset_of!(_IO_FILE, _mode) - 192usize]; - ["Offset of field: _IO_FILE::_unused3"][::core::mem::offset_of!(_IO_FILE, _unused3) - 196usize]; - ["Offset of field: _IO_FILE::_total_written"] - [::core::mem::offset_of!(_IO_FILE, _total_written) - 200usize]; - ["Offset of field: _IO_FILE::_unused2"][::core::mem::offset_of!(_IO_FILE, _unused2) - 208usize]; + ["Offset of field: _IO_FILE::_unused2"][::core::mem::offset_of!(_IO_FILE, _unused2) - 196usize]; }; -impl _IO_FILE { - #[inline] - pub fn _flags2(&self) -> libc::c_int { - unsafe { ::core::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u32) } - } - #[inline] - pub fn set__flags2(&mut self, val: libc::c_int) { - unsafe { - let val: u32 = ::core::mem::transmute(val); - self._bitfield_1.set(0usize, 24u8, val as u64) - } - } - #[inline] - pub unsafe fn _flags2_raw(this: *const Self) -> libc::c_int { - unsafe { - ::core::mem::transmute(<__BindgenBitfieldUnit<[u8; 3usize]>>::raw_get( - ::core::ptr::addr_of!((*this)._bitfield_1), - 0usize, - 24u8, - ) as u32) - } - } - #[inline] - pub unsafe fn set__flags2_raw(this: *mut Self, val: libc::c_int) { - unsafe { - let val: u32 = ::core::mem::transmute(val); - <__BindgenBitfieldUnit<[u8; 3usize]>>::raw_set( - ::core::ptr::addr_of_mut!((*this)._bitfield_1), - 0usize, - 24u8, - val as u64, - ) - } - } - #[inline] - pub fn new_bitfield_1(_flags2: libc::c_int) -> __BindgenBitfieldUnit<[u8; 3usize]> { - let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 3usize]> = Default::default(); - __bindgen_bitfield_unit.set(0usize, 24u8, { - let _flags2: u32 = unsafe { ::core::mem::transmute(_flags2) }; - _flags2 as u64 - }); - __bindgen_bitfield_unit - } -} -pub type va_list = __gnuc_va_list; unsafe extern "C" { pub fn vsnprintf( __s: *mut libc::c_char, __maxlen: libc::c_ulong, __format: *const libc::c_char, - __arg: *mut __va_list_tag, + __arg: VaList, ) -> libc::c_int; } #[repr(C)] @@ -338,7 +144,7 @@ unsafe extern "C" { unsafe extern "C" { pub fn libvlc_vprinterr( fmt: *const libc::c_char, - ap: *mut __va_list_tag, + ap: VaList, ) -> *const libc::c_char; } unsafe extern "C" { @@ -457,7 +263,7 @@ pub type libvlc_log_cb = ::core::option::Option< level: libc::c_int, ctx: *const libvlc_log_t, fmt: *const libc::c_char, - args: *mut __va_list_tag, + args: VaList, ), >; unsafe extern "C" { @@ -3404,25 +3210,3 @@ unsafe extern "C" { ppsz_options: *mut *mut libc::c_char, ); } -pub type __builtin_va_list = [__va_list_tag; 1usize]; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct __va_list_tag { - pub gp_offset: libc::c_uint, - pub fp_offset: libc::c_uint, - pub overflow_arg_area: *mut libc::c_void, - pub reg_save_area: *mut libc::c_void, -} -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of __va_list_tag"][::core::mem::size_of::<__va_list_tag>() - 24usize]; - ["Alignment of __va_list_tag"][::core::mem::align_of::<__va_list_tag>() - 8usize]; - ["Offset of field: __va_list_tag::gp_offset"] - [::core::mem::offset_of!(__va_list_tag, gp_offset) - 0usize]; - ["Offset of field: __va_list_tag::fp_offset"] - [::core::mem::offset_of!(__va_list_tag, fp_offset) - 4usize]; - ["Offset of field: __va_list_tag::overflow_arg_area"] - [::core::mem::offset_of!(__va_list_tag, overflow_arg_area) - 8usize]; - ["Offset of field: __va_list_tag::reg_save_area"] - [::core::mem::offset_of!(__va_list_tag, reg_save_area) - 16usize]; -}; diff --git a/libvlc-sys/src/lib.rs b/libvlc-sys/src/lib.rs index 12b7d82..e7ec8c3 100644 --- a/libvlc-sys/src/lib.rs +++ b/libvlc-sys/src/lib.rs @@ -3,6 +3,8 @@ #![allow(non_snake_case)] #![no_std] +pub mod valist; + // The bindings are a committed source file, regenerated out of band with // `cargo xtask bindgen`. include!("../bindings.rs"); diff --git a/libvlc-sys/src/valist.rs b/libvlc-sys/src/valist.rs new file mode 100644 index 0000000..ab7bd0e --- /dev/null +++ b/libvlc-sys/src/valist.rs @@ -0,0 +1,29 @@ +//! Platform `va_list`, extracted and simplified from `core::ffi::va_list`. +//! +//! libvlc-sys rust bindings are common to all libvlc's supported platforms and are bindgen +//! generated once by the maintainers. Hence it needs a cross-platform va_list type. +//! +//! VaLists are platform specific, the C standard only specifies that they should be possible to +//! pass by value. Libvlc uses them in the logger callback exclusively, until the rust stdlib C +//! variadic API is stable, the type with the single goal of being forwarded to a C formatting +//! function. Which simplifies greatly the implementation. +//! +//! Once the c_variadic API is stabilized, this file should be removed. + +/// [AArch64 Procedure Call Standard]: +/// https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#id110 +#[cfg(all(target_arch = "aarch64", not(target_vendor = "apple")))] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct VaList { + stack: *mut libc::c_void, + gr_top: *mut libc::c_void, + vr_top: *mut libc::c_void, + gr_offs: i32, + vr_offs: i32, +} + +/// Everywhere else: either an opaque pointer already, or a pointer to the struct the array decayed +/// into. This is sound since VaLists are only passed around. +#[cfg(not(all(target_arch = "aarch64", not(target_vendor = "apple"))))] +pub type VaList = *mut libc::c_void; diff --git a/src/core.rs b/src/core.rs index 1b4094b..dd4ed94 100644 --- a/src/core.rs +++ b/src/core.rs @@ -156,7 +156,7 @@ impl Drop for Instance { const BUF_SIZE: usize = 1024; // Write log message to the buffer by vsnprintf. unsafe extern "C" fn logging_cb( - data: *mut c_void, level: c_int, ctx: *const sys::libvlc_log_t, fmt: *const c_char, args: *mut sys::__va_list_tag) { + data: *mut c_void, level: c_int, ctx: *const sys::libvlc_log_t, fmt: *const c_char, args: sys::VaList) { let f: &Box) + Send + 'static> = ::std::mem::transmute(data); let mut buf: [c_char; BUF_SIZE] = [0; BUF_SIZE]; diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 6699e2b..012e273 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -45,7 +45,11 @@ fn generate_bindings() { .allowlist_item("(lib|LIB)?(vlc|VLC)_.*") // Required by the Windows `legacy_stdio_definitions` link workaround // (see libvlc-sys/build.rs). - .allowlist_function("vsnprintf"); + .allowlist_function("vsnprintf") + // Block the whole va_list family and rewrite the parameters bindings to our own `VaList`, + // which is ABI-correct on every target. + .blocklist_type(".*va_list.*") + .raw_line("pub use crate::valist::VaList;"); for path in &library.include_paths { bindings = bindings.clang_arg(format!("-I{}", path.display())); @@ -56,6 +60,14 @@ fn generate_bindings() { .expect("unable to generate bindings") .to_string(); + let generated = generated + .replace("*mut __va_list_tag", "VaList") + .replace(": va_list", ": VaList"); + assert!( + !generated.contains("va_list"), + "a va_list spelling we do not know about survived" + ); + std::fs::write(&output, generated).expect("couldn't write bindings"); println!("wrote {}", output.display()); }