Merge branch 'fix/valist-abi' into 'master'

libvlc-sys: fix cross-platform va_list layout

See merge request videolan/vlc-rs!20
merge-requests/20/merge
Alaric Senat 2026-08-25 14:58:49 +02:00
commit 1fbb2376ba
5 changed files with 56 additions and 229 deletions

View File

@ -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: Storage,
}
impl<Storage> __BindgenBitfieldUnit<Storage> {
#[inline]
pub const fn new(storage: Storage) -> Self {
Self { storage }
}
}
impl<Storage> __BindgenBitfieldUnit<Storage>
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::<Storage>());
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::<Storage>());
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::<Storage>());
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= core::mem::size_of::<Storage>());
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::<Storage>());
debug_assert!((bit_offset + (bit_width as usize)) / 8 <= core::mem::size_of::<Storage>());
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];
};

View File

@ -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");

View File

@ -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;

View File

@ -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<dyn Fn(LogLevel, Log, Cow<str>) + Send + 'static> = ::std::mem::transmute(data);
let mut buf: [c_char; BUF_SIZE] = [0; BUF_SIZE];

View File

@ -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());
}