Our latest adventures with bindgen

Published on 4 min read
bindgen icon
A Rust FFI bindings to C (and some C++) generator.

    As you may know, Open Source projects are a fundamental part of the software development ecosystem. While the creation of new projects is exciting and gathers a lot of attention, it is as important to maintain the already existing software that a lot of us rely on.

    Since the end of last year, we at Ferrous Systems started a partnership with ISRG to improve and contribute to bindgen. In this blog post, we will talk about why bindgen matters and the work that we have done on it for the last few months.

    Why bindgen matters

    If you've worked with Rust before, you might be familiar with the concept of *-sys crates. These crates provide FFI bindings for already existing C (and some C++) libraries.

    Take, for example, the Diesel crate, which provides object relational mapping for databases. To offer support for PostgreSQL, this crate depends on the pq-sys crate, which provides Rust bindings for libpq (the official PostgreSQL API for C).

    Writing and maintaining such bindings is not an easy task, considering that the C headers for such libraries often change between versions and platforms. Here's where bindgen is your best friend, because its main purpose is using C and C++ headers to create Rust bindings for them. It is no surprise that several *-sys crates use bindgen as a build dependency to automate this process. We are also involved in some interesting projects that rely on bindgen to interact with existing C codebases, such as the Rust-for-Linux initiative and the sudo rewrite in Rust (You can read more about both projects at Prossimo's blog posts here and here).

    This means that bindgen is a crucial dependency for several parts of the Rust ecosystem. However, being a tool used to interact with FFI, it is sometimes overlooked compared to other Rust tools. Luckily for all of us, ISRG is committed to bringing memory safe code to Internet-critical software and decided to give some love to this great tool.

    Our contributions to bindgen

    Our team has submitted 125 pull requests that have been merged into bindgen's codebase, we won't go as deeply into the details as we did in our previous post about bindgen, but will instead focus on the broader picture.

    A common issue with bindgen is that, while it offers a lot of options to control how C entities are translated into Rust, some properties of the code still require manual modifications. To help deal with this issue, we have added the following features:

    • Merge several extern blocks into a single one using the --merge-extern-blocks flag.
    • Sort the items of the generated bindings with the --sort-semantically flag.
    • Override the ABI used by extern functions with the --override-abi flag.
    • Wrap unsafe operations in unsafe blocks with the --wrap-unsafe-ops flag.
    • Manipulate source code documentation with the ParseCallback::process_comments method.
    • Detect diverging functions so they are translated as functions returning ! when the--enable-function-attribute-detection flag is enabled.
    • Format code with prettyplease using the --formatter=prettyplease flag. This is useful if bindgen is running in an environment without a rustfmt installation.

    While implementing those features, we realized that adding new configuration options to bindgen was a process that could be improved, both for simplicity and correctness. For each new option it is necessary to:

    • Add a new field to represent the new option.
    • Set the default value for the field.
    • Set the representation of the field as a CLI flag.
    • Add the methods that set this option.
    • Document the methods.

    Previously, each of these steps was done in a different part of the code. This made the learning curve of the process steeper, while remaining prone to forgetting important steps in the middle. With pull request #2473, all this has been reorganized so that only one section of code needs to be changed to do all the steps. We have also reviewed and rewritten several parts of the public and private documentation of bindgen. With this, we have made bindgen easier to contribute to.

    However, at Ferrous Systems we know that contributing to a project goes beyond opening pull requests with tons of new lines of code. Issue triaging and code reviews are as, if not more, important to the project. That's why we have coordinated with bindgen's maintainer Emilio Cobos and some of the regular contributors to the project and managed to close 95 issues and merge 48 pull requests authored by other contributors.

    We have also taken care of the new features we added. One example of this is the --wrap-static-fns flag that we discussed in our previous post has received new issues and pull requests, which tells us that it is being used and that the community cares about it.

    What's next?

    The endeavor we started with ISRG will conclude at the end of 2023, and we will dedicate the remaining time to maintaining the existing codebase, triaging issues, and reviewing pull requests. If this post has motivated you to contribute to bindgen, we'll be waiting for you at the github tracker :)