Skip to content

default ABI #2257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kcrazy opened this issue Aug 19, 2022 · 6 comments · Fixed by #2329
Closed

default ABI #2257

kcrazy opened this issue Aug 19, 2022 · 6 comments · Fixed by #2329

Comments

@kcrazy
Copy link

kcrazy commented Aug 19, 2022

the default ABI of the windows kernel is stdcall, so the most functions do not specify ABI.
Is it possible to increase the default ABI option?
example default_abi(name: &str)

bindgen::Builder::default().default_abi("stdcall"). ...;

Input C/C++ Header

// #include <ntifs.h>

#define DECLSPEC_IMPORT __declspec(dllimport)
#define NTKERNELAPI DECLSPEC_IMPORT     // wdm ntndis ntifs

NTKERNELAPI
VOID
IoDeleteDevice (
    PDEVICE_OBJECT DeviceObject
    );

Bindgen Invocation

bindgen::Builder::default()
        .header("wrapper/wrapper.h")
        .use_core()
        .derive_debug(false)
        .layout_tests(false)
        .ctypes_prefix("cty")
        .default_enum_style(bindgen::EnumVariation::ModuleConsts)
        .clang_arg(format!("-I{}", include_dir.to_str().unwrap()))
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .blocklist_type(".*")
        .allowlist_function(".*")
        .allowlist_recursively(false)
        .generate()
        .unwrap();

Actual Results

extern "C" {
    pub fn IoDeleteDevice(DeviceObject: PDEVICE_OBJECT);
}

Expected Results

extern "stdcall" {
    pub fn IoDeleteDevice(DeviceObject: PDEVICE_OBJECT);
}
@pvdrz
Copy link
Contributor

pvdrz commented Oct 4, 2022

I dug a bit around this issue and couldn't figure out when clang actually decides to use the Default calling convention. Every example I tried in linux and windows had the C calling convention and there was no way to distinguish that from functions tagged with the cdecl attribute for example.

I guess one alternative would be to do this in postprocessing (or even inside codegen I guess) and add a new family of regex options to bindgen so we can set the ABI to whatever we want but I'm not sure that's the right approach as it would change the calling convention arbitrarily without respecting the target or the header inputs.

Any ideas @emilio, @kulp, @amanjeev?

@emilio
Copy link
Contributor

emilio commented Oct 16, 2022

Yeah, so right now we make the assumption that the default ABI is "C":

CXCallingConv_Default => Abi::C,

Ideally we'd just do the right thing based on the target. If clang exposes the resolved "default" ABI that'd be preferrable to adding a new option.

@pvdrz
Copy link
Contributor

pvdrz commented Oct 18, 2022

But AFAIK CXCallingConv_Default is not actually returned anywhere by libclang.

Edit: Just wanted to make it clear that I changed exactly that line expecting it would work but it does not :(. Apparently what happens is that this CXCallingConv_Default variant is defined by clang but not returned anywhere at all so everything is tagged as using the C calling convention.

@pvdrz
Copy link
Contributor

pvdrz commented Oct 18, 2022

Yep, if you check libclang's code, you'll notice that clang_getFunctionTypeCallingConv actually calls getCallConv, which at the same time calls getCC, this last function returns a CallingConv value (this enum doesn't even have a "default calling convention" variant). This value is built from a Bits field that by default sets the calling convention to CC_C.

So no idea where this CXCallingConv_Default value is used and how.

As a consequence. I think there's no way to distingush a function without any calling convention attributes and one with the cdecl calling convention attribute.

@pvdrz
Copy link
Contributor

pvdrz commented Oct 24, 2022

Given that it seems there's no way to override the default calling convention without changing items that explicitly use the C calling convention I wonder if we could add a regex option like this:

builder.override_abi("matching_function", ABI::Stdcall);

so this input:

void matching_function();
void other_function();

emits this output:

extern "stdcall" {
    fn matching_function();
}
extern "C" {
    fn other_function();
}

This would also fix #2224

pvdrz added a commit to ferrous-systems/rust-bindgen that referenced this issue Nov 1, 2022
This option can be used from the CLI with the <abi>:<regex> syntax and
it overrides the ABI of a function if it matches <regex>.

Fixes rust-lang#2257 as the `".*"` regex can be used to match every function.
pvdrz added a commit that referenced this issue Nov 2, 2022
* Add the `--override-abi` option.

This option can be used from the CLI with the <abi>:<regex> syntax and
it overrides the ABI of a function if it matches <regex>.

Fixes #2257
@mmotejlek
Copy link

Hi, I've been expermenting with Windows driver APIs and Bindgen and stumbled into this thread.

I'm not sure if it's relevant but here are my observations. I'm very new to this so please excuse me if I'm stating the obvious or doing something wrong.

  • Bindgen generates extern "C" for stdcall when compiling for x64, but extern "system" for x86.
  • Using .clang_arg("-mrtd") (stdcall by default) and compiling for x86 seems to generate extern "system" by default.
  • Using .clang_arg("-mrtd") and compiling for x64 results in a libclang error.

The wrapper file:

#include <10.0.22621.0/um/windows.h>

#include <wdf/umdf/2.33/wdf.h>

// Needs to be compiled as C++
#define IDD_STUB
#include <10.0.22621.0/um/iddcx/1.9/IddCx.h>

and relevant snippets from build.rs (commented lines are variants):

println!("cargo:rustc-link-search=C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64/iddcx/1.9");

// println!("cargo:rustc-link-search=C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x86/iddcx/1.9");

println!("cargo:rustc-link-lib=iddcxstub");

bindgen::Builder::default()
        // .clang_arg("--target=i686-pc-windows-msvc")
        // .clang_arg("-mrtd")
        .clang_arg("--language=c++")
        .clang_arg("--include-directory=C:/Program Files (x86)/Windows Kits/10/Include")
        .header("wrapper.hpp")
        .allowlist_type("DRIVER_INITIALIZE")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .use_core()
        .generate()

As far as I understand, compiling for x64 doesn't differentiate between cdecl and stdcall because they are converted to a different calling convention. So if I understand correctly, I should be able to use extern "C" against Windows API no matter if it uses cdecl or stdcall as long as I'm compiling for x64?

Also regarding:

the default ABI of the windows kernel is stdcall, so the most functions do not specify ABI.

I'd like to ask where one learns this kind of info because searching for a confirmation of this is what lead me here in the first place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants