rust-bindgen icon indicating copy to clipboard operation
rust-bindgen copied to clipboard

Bindgen generates different type name for va_list on different systems

Open Morganamilo opened this issue 2 years ago • 9 comments

Input C/C++ Header

typedef __builtin_va_list va_list;
void foo(va_list arg);

Bindgen Invocation

$ bindgen input.h

Actual Results

  • on arm64 macos
/* automatically generated by rust-bindgen 0.68.1 */

pub type va_list = __builtin_va_list;
extern "C" {
    pub fn foo(arg: va_list);
}
pub type __builtin_va_list = *mut ::std::os::raw::c_char;
  • on x64_64 linux
/* automatically generated by rust-bindgen 0.68.1 */

pub type va_list = __builtin_va_list;
extern "C" {
    pub fn foo(arg: *mut __va_list_tag);
}
pub type __builtin_va_list = [__va_list_tag; 1usize];
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct __va_list_tag {
    pub gp_offset: ::std::os::raw::c_uint,
    pub fp_offset: ::std::os::raw::c_uint,
    pub overflow_arg_area: *mut ::std::os::raw::c_void,
    pub reg_save_area: *mut ::std::os::raw::c_void,
}

Expected Results

I expect the name of this type to be the same on both systems regardless of the underlying implementation.

Morganamilo avatar Sep 07 '23 13:09 Morganamilo

I can confirm this issue, any workaround?

I was trying to use https://github.com/dylanmckay/vsprintf for a function that takes const char* format, va_list args and this works as expected on arm64 but not on x64_64 linux

118 |         let str = vsprintf::vsprintf(format, args).unwrap();
    |                   ------------------         ^^^^ expected `*mut _`, found `[__va_list_tag; 1]`
    |                   |
    |                   arguments to this function are incorrect
    |
    = note: expected raw pointer `*mut _`
                     found array `[libpinmame::__va_list_tag; 1]`

francisdb avatar Sep 09 '23 19:09 francisdb

You can test this locally

eg on aarch64

rustup target add x86_64-apple-darwin
bindgen input.h -- --target=x86_64-apple-darwin
#  --target=aarch64-apple-darwin

for now I'm working around as vsprintf can handle both types

#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
type VaListType = *mut crate::libpinmame::__va_list_tag;
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
type VaListType = crate::libpinmame::va_list;

pub unsafe extern "C" fn pinmame_on_log_message_callback(
    log_level: u32,
    format: *const ::std::os::raw::c_char,
    args: VaListType,
    _user_data: *const ::std::os::raw::c_void,
) {
    let str = unsafe { vsprintf::vsprintf(format, args).unwrap() };
    on_log_message(log_level, str);
}

Not sure if this also applies to linux/windows aarch64. You can also use --target=... on the cargo build to build for a different architecture.

If some of the devs could confirm this is not a bug but just something we need to handle?

francisdb avatar Sep 14 '23 12:09 francisdb

My packages build out the box on linux/arm64 so this seams to be just a mac thing.

Morganamilo avatar Sep 21 '23 01:09 Morganamilo

While I understand this is annoying, there is not much we can do from the bindgen side as clang provides the definitions for these built-in types itself. So the fact that bindgen emits the __builtin_va_list type as an alias for [__va_list_tag; 1usize] on x86_64 is just because that's the definition that clang is exposing, the same logic applies to the aarch64 case. You could use @francisdb 's workaround or blocklist __builtin_va_list and replace it with the alias you consider appropriate.

pvdrz avatar Sep 25 '23 04:09 pvdrz

The type name is the same in all c (even if the type is a macro). Is there any way bingen could emit a type elias that's consistent regardless of the underlying type?

Morganamilo avatar Sep 25 '23 04:09 Morganamilo

So after a little bit more digging it seems the issue we have here is that on C void foo(__va_list_tag arg[1]); is just the same as void foo(__va_list_tag *arg);. A more minimal example would be

void foo(int arg[2]);

which is translated as

pub fn foo(arg: *mut ::std::os::raw::c_int);

instead of

pub fn foo(arg: *mut [::std::os::raw::c_int; 2]);

So this is a particular case of https://github.com/rust-lang/rust-bindgen/issues/1561 which was fixed by adding the --use-array-pointers-in-arguments option here https://github.com/rust-lang/rust-bindgen/pull/1564.

If I pass this flag to bindgen using the headers you provided, on linux in x86_64 I get the following output:

pub type va_list = __builtin_va_list;
extern "C" {
    pub fn foo(arg: *mut va_list);
}
pub type __builtin_va_list = [__va_list_tag; 1usize];

Which I think is what you want. Let me know if this is correct or not :)

pvdrz avatar Sep 25 '23 16:09 pvdrz

That flags changes args: *mut va_list to args: *mut __va_list_tag on x86_64 but on arm it stays as arg: mut va_list.

Morganamilo avatar Oct 12 '23 09:10 Morganamilo

yeah but the name of the type is the same, isn't that what you wanted?

pvdrz avatar Oct 17 '23 14:10 pvdrz

No, I have my own rust function that takes a VaList then calls a c function that takes a VaList

extern "C" fn my_function(args: ?) {
	bindgen_generated_function(args)
}

I need to be able to declare my function with the right type for the types to match.

Morganamilo avatar Oct 17 '23 15:10 Morganamilo