| #![doc(html_root_url = "https://docs.rs/libffi-sys/2.3.0")] |
| //! Low-level Rust bindings for [libffi](https://sourceware.org/libffi/) |
| //! |
| //! The C libffi library provides two main facilities: assembling calls |
| //! to functions dynamically, and creating closures that can be called |
| //! as ordinary C functions. |
| //! |
| //! This is an undocumented wrapper, originally generated by bindgen then |
| //! cleaned up manually, intended as the basis for higher-level bindings. |
| //! |
| //! See [the libffi crate](https://crates.io/crates/libffi/) for a |
| //! higher-level API. |
| //! |
| //! # Usage |
| //! |
| //! `libffi-sys` can either build its own copy of the libffi C library [from |
| //! github](https://github.com/libffi/libffi) or it can link against your |
| //! system’s C libffi. By default it builds its own because many systems |
| //! ship with an old C libffi; this requires that you have a working make, |
| //! C compiler, automake, and autoconf first. If your system libffi |
| //! is new enough (v3.2.1 as of October 2019), you can instead enable the |
| //! `system` feature flag to use that. If you want this crate to build |
| //! a C libffi for you, add |
| //! |
| //! ```toml |
| //! [dependencies] |
| //! libffi-sys = "2.3.0" |
| //! ``` |
| //! |
| //! to your `Cargo.toml`. If you want to use your system C libffi, then |
| //! |
| //! ```toml |
| //! [dependencies.libffi-sys] |
| //! version = "2.3.0" |
| //! features = ["system"] |
| //! ``` |
| //! |
| //! to your `Cargo.toml` instead. |
| //! |
| //! This crate supports Rust version 1.32 and later. |
| |
| #![allow(non_camel_case_types)] |
| #![allow(non_snake_case)] |
| #![allow(non_upper_case_globals)] |
| #![allow(improper_ctypes)] |
| #![allow(unused_imports)] |
| |
| use std::fmt::{self, Debug}; |
| use std::mem::zeroed; |
| use std::os::raw::{c_char, c_int, c_long, c_schar, c_uint, c_ulong, c_ushort, c_void}; |
| |
| mod arch; |
| pub use arch::*; |
| use fmt::Formatter; |
| |
| pub type ffi_arg = c_ulong; |
| pub type ffi_sarg = c_long; |
| pub type ffi_abi = u32; |
| pub type ffi_status = u32; |
| pub type ffi_type_enum = u32; |
| |
| pub const FFI_64_BIT_MAX: u64 = 9223372036854775807; |
| pub const FFI_CLOSURES: u32 = 1; |
| pub const FFI_SIZEOF_ARG: usize = std::mem::size_of::<c_long>(); |
| // NOTE: This only differs from FFI_SIZEOF_ARG on ILP platforms, which Rust does not support |
| pub const FFI_SIZEOF_JAVA_RAW: usize = FFI_SIZEOF_ARG; |
| |
| pub const FFI_TYPE_VOID: u32 = 0; |
| pub const FFI_TYPE_INT: u32 = 1; |
| pub const FFI_TYPE_FLOAT: u32 = 2; |
| pub const FFI_TYPE_DOUBLE: u32 = 3; |
| pub const FFI_TYPE_LONGDOUBLE: u32 = 4; |
| pub const FFI_TYPE_UINT8: u32 = 5; |
| pub const FFI_TYPE_SINT8: u32 = 6; |
| pub const FFI_TYPE_UINT16: u32 = 7; |
| pub const FFI_TYPE_SINT16: u32 = 8; |
| pub const FFI_TYPE_UINT32: u32 = 9; |
| pub const FFI_TYPE_SINT32: u32 = 10; |
| pub const FFI_TYPE_UINT64: u32 = 11; |
| pub const FFI_TYPE_SINT64: u32 = 12; |
| pub const FFI_TYPE_STRUCT: u32 = 13; |
| pub const FFI_TYPE_POINTER: u32 = 14; |
| pub const FFI_TYPE_COMPLEX: u32 = 15; |
| pub const FFI_TYPE_LAST: u32 = 15; |
| |
| pub const ffi_status_FFI_OK: ffi_status = 0; |
| pub const ffi_status_FFI_BAD_TYPEDEF: ffi_status = 1; |
| pub const ffi_status_FFI_BAD_ABI: ffi_status = 2; |
| pub const ffi_status_FFI_BAD_ARGTYPE: ffi_status = 3; |
| |
| pub const ffi_type_enum_STRUCT: ffi_type_enum = 13; |
| pub const ffi_type_enum_COMPLEX: ffi_type_enum = 15; |
| |
| #[repr(C)] |
| #[derive(Debug, Copy, Clone)] |
| pub struct ffi_type { |
| pub size: usize, |
| pub alignment: c_ushort, |
| pub type_: c_ushort, |
| pub elements: *mut *mut ffi_type, |
| } |
| |
| impl Default for ffi_type { |
| fn default() -> Self { |
| unsafe { zeroed() } |
| } |
| } |
| |
| #[repr(C)] |
| #[derive(Debug, Copy, Clone)] |
| pub struct ffi_cif { |
| pub abi: ffi_abi, |
| pub nargs: c_uint, |
| pub arg_types: *mut *mut ffi_type, |
| pub rtype: *mut ffi_type, |
| pub bytes: c_uint, |
| pub flags: c_uint, |
| #[cfg(all(target_arch = "aarch64", target_os = "windows"))] |
| pub is_variadic: c_uint, |
| #[cfg(all(target_arch = "aarch64", target_vendor = "apple"))] |
| pub aarch64_nfixedargs: c_uint, |
| #[cfg(all(target_arch = "arm"))] |
| pub vfp_used: c_int, |
| #[cfg(all(target_arch = "arm"))] |
| pub vfp_reg_free: c_ushort, |
| #[cfg(all(target_arch = "arm"))] |
| pub vfp_nargs: c_ushort, |
| #[cfg(all(target_arch = "arm"))] |
| pub vfp_args: [c_schar; 16], |
| #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] |
| pub nfixedargs: c_uint, |
| #[cfg(any(target_arch = "riscv", target_arch = "riscv64"))] |
| pub riscv_nfixedargs: c_uint, |
| #[cfg(any(target_arch = "riscv", target_arch = "riscv64"))] |
| pub riscv_unused: c_uint, |
| #[cfg(all(target_arch = "loongarch64"))] |
| pub loongarch_nfixedargs: c_uint, |
| #[cfg(all(target_arch = "loongarch64"))] |
| pub loongarch_unused: c_uint, |
| } |
| |
| impl Default for ffi_cif { |
| fn default() -> Self { |
| unsafe { zeroed() } |
| } |
| } |
| |
| #[repr(C, align(64))] |
| #[derive(Copy, Clone)] |
| pub union ffi_raw { |
| pub sint: ffi_sarg, |
| pub uint: ffi_arg, |
| pub flt: f32, |
| pub data: [c_char; FFI_SIZEOF_ARG], |
| pub ptr: *mut c_void, |
| } |
| |
| impl Default for ffi_raw { |
| fn default() -> Self { |
| unsafe { zeroed() } |
| } |
| } |
| |
| pub type ffi_java_raw = ffi_raw; |
| |
| #[repr(C, align(64))] |
| #[derive(Copy, Clone)] |
| pub union ffi_trampoline { |
| pub tramp: [c_char; FFI_TRAMPOLINE_SIZE], |
| pub ftramp: *mut c_void, |
| } |
| |
| #[repr(C)] |
| #[derive(Copy, Clone)] |
| pub struct ffi_closure { |
| pub tramp: ffi_trampoline, |
| pub cif: *mut ffi_cif, |
| pub fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut *mut c_void, |
| arg4: *mut c_void, |
| ), |
| >, |
| pub user_data: *mut c_void, |
| } |
| |
| /// Implements Debug manually since sometimes FFI_TRAMPOLINE_SIZE is too large |
| impl Debug for ffi_closure { |
| fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
| f.debug_struct("ffi_closure") |
| .field("tramp", unsafe { &&self.tramp.tramp[..] }) |
| .field("cif", &self.cif) |
| .field("fun", &self.fun) |
| .field("user_data", &self.user_data) |
| .finish() |
| } |
| } |
| |
| impl Default for ffi_closure { |
| fn default() -> Self { |
| unsafe { zeroed() } |
| } |
| } |
| |
| #[repr(C)] |
| #[derive(Copy, Clone)] |
| pub struct ffi_raw_closure { |
| pub tramp: [c_char; FFI_TRAMPOLINE_SIZE], |
| pub cif: *mut ffi_cif, |
| // See: https://github.com/libffi/libffi/blob/3a7580da73b7f16f275277316d00e3497cbb5a8c/include/ffi.h.in#L364 |
| #[cfg(not(target_arch = "i686"))] |
| pub translate_args: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut *mut c_void, |
| arg4: *mut c_void, |
| ), |
| >, |
| #[cfg(not(target_arch = "i686"))] |
| pub this_closure: *mut c_void, |
| pub fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut ffi_raw, |
| arg4: *mut c_void, |
| ), |
| >, |
| pub user_data: *mut c_void, |
| } |
| |
| /// Implements Debug manually since sometimes FFI_TRAMPOLINE_SIZE is too large |
| impl Debug for ffi_raw_closure { |
| fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
| let mut debug_struct = f.debug_struct("ffi_raw_closure"); |
| debug_struct |
| .field("tramp", &&self.tramp[..]) |
| .field("cif", &self.cif); |
| |
| #[cfg(not(target_arch = "i686"))] |
| debug_struct.field("translate_args", &self.translate_args); |
| #[cfg(not(target_arch = "i686"))] |
| debug_struct.field("this_closure", &self.this_closure); |
| |
| debug_struct |
| .field("fun", &self.fun) |
| .field("user_data", &self.user_data) |
| .finish() |
| } |
| } |
| |
| impl Default for ffi_raw_closure { |
| fn default() -> Self { |
| unsafe { zeroed() } |
| } |
| } |
| #[repr(C)] |
| #[derive(Copy, Clone)] |
| pub struct ffi_java_raw_closure { |
| pub tramp: [c_char; FFI_TRAMPOLINE_SIZE], |
| pub cif: *mut ffi_cif, |
| // See: https://github.com/libffi/libffi/blob/3a7580da73b7f16f275277316d00e3497cbb5a8c/include/ffi.h.in#L390 |
| #[cfg(not(target_arch = "i686"))] |
| pub translate_args: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut *mut c_void, |
| arg4: *mut c_void, |
| ), |
| >, |
| #[cfg(not(target_arch = "i686"))] |
| pub this_closure: *mut c_void, |
| pub fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut ffi_java_raw, |
| arg4: *mut c_void, |
| ), |
| >, |
| pub user_data: *mut c_void, |
| } |
| |
| /// Implements Debug manually since sometimes FFI_TRAMPOLINE_SIZE is too large |
| impl Debug for ffi_java_raw_closure { |
| fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
| let mut debug_struct = f.debug_struct("ffi_java_raw_closure"); |
| debug_struct |
| .field("tramp", &&self.tramp[..]) |
| .field("cif", &self.cif); |
| |
| #[cfg(not(target_arch = "i686"))] |
| debug_struct.field("translate_args", &self.translate_args); |
| #[cfg(not(target_arch = "i686"))] |
| debug_struct.field("this_closure", &self.this_closure); |
| |
| debug_struct |
| .field("fun", &self.fun) |
| .field("user_data", &self.user_data) |
| .finish() |
| } |
| } |
| |
| impl Default for ffi_java_raw_closure { |
| fn default() -> Self { |
| unsafe { zeroed() } |
| } |
| } |
| |
| #[repr(C)] |
| #[derive(Debug, Copy, Clone)] |
| pub struct ffi_go_closure { |
| pub tramp: *mut c_void, |
| pub cif: *mut ffi_cif, |
| pub fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut *mut c_void, |
| arg4: *mut c_void, |
| ), |
| >, |
| } |
| impl Default for ffi_go_closure { |
| fn default() -> Self { |
| unsafe { zeroed() } |
| } |
| } |
| |
| extern "C" { |
| pub static mut ffi_type_void: ffi_type; |
| pub static mut ffi_type_uint8: ffi_type; |
| pub static mut ffi_type_sint8: ffi_type; |
| pub static mut ffi_type_uint16: ffi_type; |
| pub static mut ffi_type_sint16: ffi_type; |
| pub static mut ffi_type_uint32: ffi_type; |
| pub static mut ffi_type_sint32: ffi_type; |
| pub static mut ffi_type_uint64: ffi_type; |
| pub static mut ffi_type_sint64: ffi_type; |
| pub static mut ffi_type_float: ffi_type; |
| pub static mut ffi_type_double: ffi_type; |
| pub static mut ffi_type_pointer: ffi_type; |
| |
| #[cfg(not(target_arch = "aarch64"))] |
| #[cfg(not(all(target_arch = "arm", target_os = "linux", target_env = "gnu")))] |
| pub static mut ffi_type_longdouble: ffi_type; |
| |
| #[cfg(feature = "complex")] |
| pub static mut ffi_type_complex_float: ffi_type; |
| |
| #[cfg(feature = "complex")] |
| pub static mut ffi_type_complex_double: ffi_type; |
| |
| #[cfg(feature = "complex")] |
| #[cfg(not(all(target_arch = "arm", target_os = "linux", target_env = "gnu")))] |
| pub static mut ffi_type_complex_longdouble: ffi_type; |
| |
| pub fn ffi_raw_call( |
| cif: *mut ffi_cif, |
| fn_: Option<unsafe extern "C" fn()>, |
| rvalue: *mut c_void, |
| avalue: *mut ffi_raw, |
| ); |
| |
| pub fn ffi_ptrarray_to_raw(cif: *mut ffi_cif, args: *mut *mut c_void, raw: *mut ffi_raw); |
| |
| pub fn ffi_raw_to_ptrarray(cif: *mut ffi_cif, raw: *mut ffi_raw, args: *mut *mut c_void); |
| |
| pub fn ffi_raw_size(cif: *mut ffi_cif) -> usize; |
| |
| // See: https://github.com/libffi/libffi/blob/3a7580da73b7f16f275277316d00e3497cbb5a8c/include/ffi.h.in#L286 |
| #[cfg(not(target_arch = "i686"))] |
| pub fn ffi_java_raw_call( |
| cif: *mut ffi_cif, |
| fn_: Option<unsafe extern "C" fn()>, |
| rvalue: *mut c_void, |
| avalue: *mut ffi_java_raw, |
| ); |
| |
| pub fn ffi_java_ptrarray_to_raw( |
| cif: *mut ffi_cif, |
| args: *mut *mut c_void, |
| raw: *mut ffi_java_raw, |
| ); |
| |
| pub fn ffi_java_raw_to_ptrarray( |
| cif: *mut ffi_cif, |
| raw: *mut ffi_java_raw, |
| args: *mut *mut c_void, |
| ); |
| |
| pub fn ffi_java_raw_size(cif: *mut ffi_cif) -> usize; |
| |
| pub fn ffi_closure_alloc(size: usize, code: *mut *mut c_void) -> *mut c_void; |
| |
| pub fn ffi_closure_free(arg1: *mut c_void); |
| |
| pub fn ffi_prep_closure( |
| arg1: *mut ffi_closure, |
| arg2: *mut ffi_cif, |
| fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut *mut c_void, |
| arg4: *mut c_void, |
| ), |
| >, |
| user_data: *mut c_void, |
| ) -> ffi_status; |
| |
| pub fn ffi_prep_closure_loc( |
| arg1: *mut ffi_closure, |
| arg2: *mut ffi_cif, |
| fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut *mut c_void, |
| arg4: *mut c_void, |
| ), |
| >, |
| user_data: *mut c_void, |
| codeloc: *mut c_void, |
| ) -> ffi_status; |
| |
| pub fn ffi_prep_raw_closure( |
| arg1: *mut ffi_raw_closure, |
| cif: *mut ffi_cif, |
| fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut ffi_raw, |
| arg4: *mut c_void, |
| ), |
| >, |
| user_data: *mut c_void, |
| ) -> ffi_status; |
| |
| pub fn ffi_prep_raw_closure_loc( |
| arg1: *mut ffi_raw_closure, |
| cif: *mut ffi_cif, |
| fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut ffi_raw, |
| arg4: *mut c_void, |
| ), |
| >, |
| user_data: *mut c_void, |
| codeloc: *mut c_void, |
| ) -> ffi_status; |
| |
| // See: https://github.com/libffi/libffi/blob/3a7580da73b7f16f275277316d00e3497cbb5a8c/include/ffi.h.in#L419 |
| #[cfg(not(target_arch = "i686"))] |
| pub fn ffi_prep_java_raw_closure( |
| arg1: *mut ffi_java_raw_closure, |
| cif: *mut ffi_cif, |
| fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut ffi_java_raw, |
| arg4: *mut c_void, |
| ), |
| >, |
| user_data: *mut c_void, |
| ) -> ffi_status; |
| |
| // See: https://github.com/libffi/libffi/blob/3a7580da73b7f16f275277316d00e3497cbb5a8c/include/ffi.h.in#L419 |
| #[cfg(not(target_arch = "i686"))] |
| pub fn ffi_prep_java_raw_closure_loc( |
| arg1: *mut ffi_java_raw_closure, |
| cif: *mut ffi_cif, |
| fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut ffi_java_raw, |
| arg4: *mut c_void, |
| ), |
| >, |
| user_data: *mut c_void, |
| codeloc: *mut c_void, |
| ) -> ffi_status; |
| |
| pub fn ffi_prep_go_closure( |
| arg1: *mut ffi_go_closure, |
| arg2: *mut ffi_cif, |
| fun: Option< |
| unsafe extern "C" fn( |
| arg1: *mut ffi_cif, |
| arg2: *mut c_void, |
| arg3: *mut *mut c_void, |
| arg4: *mut c_void, |
| ), |
| >, |
| ) -> ffi_status; |
| |
| pub fn ffi_call_go( |
| cif: *mut ffi_cif, |
| fn_: Option<unsafe extern "C" fn()>, |
| rvalue: *mut c_void, |
| avalue: *mut *mut c_void, |
| closure: *mut c_void, |
| ); |
| |
| pub fn ffi_prep_cif( |
| cif: *mut ffi_cif, |
| abi: ffi_abi, |
| nargs: c_uint, |
| rtype: *mut ffi_type, |
| atypes: *mut *mut ffi_type, |
| ) -> ffi_status; |
| |
| pub fn ffi_prep_cif_var( |
| cif: *mut ffi_cif, |
| abi: ffi_abi, |
| nfixedargs: c_uint, |
| ntotalargs: c_uint, |
| rtype: *mut ffi_type, |
| atypes: *mut *mut ffi_type, |
| ) -> ffi_status; |
| |
| pub fn ffi_call( |
| cif: *mut ffi_cif, |
| fn_: Option<unsafe extern "C" fn()>, |
| rvalue: *mut c_void, |
| avalue: *mut *mut c_void, |
| ); |
| |
| pub fn ffi_get_struct_offsets( |
| abi: ffi_abi, |
| struct_type: *mut ffi_type, |
| offsets: *mut usize, |
| ) -> ffi_status; |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| extern "C" fn add(x: u64, y: u64) -> u64 { |
| x + y |
| } |
| |
| #[test] |
| fn test_function_with_two_arguments() { |
| unsafe { |
| let mut cif: ffi_cif = Default::default(); |
| let mut arg_types: Vec<*mut ffi_type> = |
| vec![&mut ffi_type_uint64, &mut ffi_type_uint64]; |
| |
| let prep_status = ffi_prep_cif( |
| &mut cif, |
| ffi_abi_FFI_DEFAULT_ABI, |
| 2, |
| &mut ffi_type_uint64, |
| arg_types.as_mut_ptr(), |
| ); |
| |
| assert_eq!(prep_status, ffi_status_FFI_OK); |
| |
| let mut rval = 0u64; |
| let func = &*(&(add as *mut extern "C" fn(u64, u64) -> u64) as *const _ |
| as *const extern "C" fn()); |
| |
| ffi_call( |
| &mut cif, |
| Some(*func), |
| &mut rval as *mut _ as *mut c_void, |
| vec![ |
| &mut 4u64 as *mut _ as *mut c_void, |
| &mut 5u64 as *mut _ as *mut c_void, |
| ] |
| .as_mut_ptr(), |
| ); |
| |
| assert_eq!(rval, 9); |
| } |
| } |
| } |