Skip to content

Commit

Permalink
virtio-bindings: use bindgen library from build.rs
Browse files Browse the repository at this point in the history
Since commit 3e51e2d ("virtio-bindings: regenerate with bindgen 0.70.1")
there are compile-time alignment checks in virtio-bindings code. These
checks are specific to the architecture where bindgen was run and they
can break builds on other architectures.

Here is an example i686 build failure after upgrading from
virtio-bindings 0.2.2 to 0.2.4:
https://koji.fedoraproject.org/koji/taskinfo?taskID=127997322

The bindgen User Guide recommends generating bindings from build.rs
because it avoids portability issues and the need to maintain a copy of
the bindings for each target:
https://rust-lang.github.io/rust-bindgen/library-usage.html

Introduce an import-linux-headers.sh script that copies required virtio
headers from a Linux headers tree into include/. Do not distribute the
full Linux headers tree because it is large and has falls under various
software licenses that are not compatible with this crate's license.

Generate the actual bindings at compile-time in build.rs and then
include!() the output from src/lib.rs.

You can verify that the generated bindings from Linux 6.12 have not
changed significantly for x86_64 by diffing them against the previous
commit. For example:
--- orig/virtio_gpu.rs
+++ new/virtio_gpu.rs
@@ -1,4 +1,4 @@
-/* automatically generated by rust-bindgen 0.70.1 */
+/* automatically generated by rust-bindgen 0.71.1 */

 #[repr(C)]
 #[derive(Default)]
@@ -58,7 +58,7 @@
 pub const VIRTIO_GPU_MAP_CACHE_WC: u32 = 3;
 pub type __u8 = ::std::os::raw::c_uchar;
 pub type __u32 = ::std::os::raw::c_uint;
-pub type __u64 = ::std::os::raw::c_ulonglong;
+pub type __u64 = ::std::os::raw::c_ulong;
 pub type __le32 = __u32;
 pub type __le64 = __u64;
 pub const virtio_gpu_ctrl_type_VIRTIO_GPU_UNDEFINED: virtio_gpu_ctrl_type = 0

The __u64 change is due to how <linux/types.h> defined __u64. We should
avoid using that non-BSD licensed header. The result is equivalent now
that the bindings are generated at compile-time.

Fixes: #326
Signed-off-by: Stefan Hajnoczi <[email protected]>
  • Loading branch information
stefanhaRH committed Jan 30, 2025
1 parent 67d5be7 commit 31f7c73
Show file tree
Hide file tree
Showing 29 changed files with 2,407 additions and 2,931 deletions.
4 changes: 4 additions & 0 deletions virtio-bindings/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
## Changed

- Regenerate bindings with Linux 6.12.
- Introduced bindgen build dependency and its clang development package
dependency. See bindgen fix below for why this was necessary.

## Fixed

- Add license files.
- Use bindgen library from build.rs to fix i686 builds due to x86_64-specific
alignment checks.

# v0.2.4

Expand Down
76 changes: 32 additions & 44 deletions virtio-bindings/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,59 +1,47 @@
# Contributing to virtio-bindings

## Dependencies
## Overview

### Bindgen
The bindings are currently generated using
[bindgen](https://rust-lang.github.io/rust-bindgen/) version 0.70.1:
```bash
cargo install bindgen-cli --vers 0.70.1
```
virtio-bindings is periodically updated with imported virtio headers from the
Linux kernel. The Linux header files have kernel header dependencies that are
removed during import so that bindgen can process them in isolation without a
full set of kernel headers. This is also necessary because the licenses of
individual kernel header files varies and we only want to distribute
BSD-licensed virtio headers.

### Linux Kernel
## Importing kernel headers
Generating bindings depends on the Linux kernel, so you need to have the
repository on your machine:

```bash
git clone https://github.com/torvalds/linux.git
```

## Example for updating to a new kernel version
Install the headers so they can be used for import:
```bash
cd linux
git checkout <linux-version>
make headers_install INSTALL_HDR_PATH=headers-<linux-version>
```

For this example we assume that you have both linux and virtio-bindings
repositories in your root.
Import kernel headers into `include/`:
```bash
cd ~/vm-virtio/virtio-bindings
./import-linux-headers.sh path/to/headers-<linux-version>
```

Test that the build still works:
```bash
# linux is the repository that you cloned at the previous step.
cd linux
# Step 1: Checkout the version you want to generate the bindings for.
git checkout v5.0

# Step 2: Generate the bindings from the kernel headers. We need to generate a
# file for each one of the virtio headers. For the moment, we are only picking
# headers that we are interested in. Feel free to add additional header files if
# you need them for your project.
make headers_install INSTALL_HDR_PATH=v5_0_headers
cd v5_0_headers
for i in \
virtio_blk \
virtio_config \
virtio_gpu \
virtio_ids \
virtio_input \
virtio_mmio \
virtio_net \
virtio_ring \
virtio_scsi \
; do \
bindgen include/linux/$i.h -o $i.rs \
--allowlist-file include/linux/$i.h \
--with-derive-default \
--with-derive-partialeq \
-- -Iinclude
done
cd ~

# Step 6: Copy the generated files to the new version module.
cp linux/v5_0_headers/*.rs vm-virtio/virtio-bindings/src
mv vm-virtio/virtio-bindings/src/virtio_net.rs vm-virtio/virtio-bindings/src/virtio_net/generated.rs
cargo build
```

## Adding bindings for new header files
New kernel headers can be added as follows:
1. Add the new file to import-linux-headers.sh so it is imported from the Linux
kernel header directory into include/.
2. Add the new file to build.rs so bindgen generates bindings.
3. Add the new module to src/lib.rs so the generated bindings are exposed in
the crate.
4. Check if `cargo build` still succeeds. If the header has new kernel header
dependencies then you need to add them (if they are BSD licensed) or stub
them out (if they are not BSD licensed).
3 changes: 3 additions & 0 deletions virtio-bindings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ license = "BSD-3-Clause OR Apache-2.0"
virtio-v4_14_0 = []
virtio-v5_0_0 = []

[build-dependencies]
bindgen = "0.71.0"

[dependencies]
39 changes: 39 additions & 0 deletions virtio-bindings/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright Red Hat, Inc.
// SPDX-License-Identifier: (BSD-3-Clause OR Apache-2.0)

use std::env;
use std::path::PathBuf;

fn generate_bindings(header: &str) {
let bindings = bindgen::Builder::default()
.header(format!("include/linux/{header}.h"))
.allowlist_file(format!("include/linux/{header}.h"))
.derive_default(true)
.derive_partialeq(true)
.clang_arg("-Iinclude")
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.unwrap_or_else(|_| panic!("Unable to generate bindings for {header}"));

let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join(format!("{header}.rs")))
.unwrap_or_else(|_| panic!("Couldn't write bindings for {header}"));
}

fn main() {
// If you change this list, remember to update src/lib.rs
for header in [
"virtio_blk",
"virtio_config",
"virtio_gpu",
"virtio_ids",
"virtio_input",
"virtio_mmio",
"virtio_net",
"virtio_ring",
"virtio_scsi",
] {
generate_bindings(header);
}
}
34 changes: 34 additions & 0 deletions virtio-bindings/import-linux-headers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/bash
# SPDX-License-Identifier: (BSD-3-Clause OR Apache-2.0)
# Copyright Red Hat, Inc.
#
# ./import-linux-headers.sh path/to/kernel/headers
#
# Import header files from a Linux kernel header tree. Be sure to run `cargo
# build` to test that bindgen succeeds before committing the updated headers.

src=$1
dst=include/linux

import_header() {
# Use our local header files rather than system headers
sed -e 's%#include <\([^>]*\)>%#include "\1"%' "$1" >"$2"
}

mkdir -p "$dst"

# If you change this list, remember to update build.rs
for header in \
version \
virtio_blk \
virtio_config \
virtio_gpu \
virtio_ids \
virtio_input \
virtio_mmio \
virtio_net \
virtio_ring \
virtio_scsi \
virtio_types; do
import_header "$src/include/linux/$header.h" "$dst/$header.h"
done
5 changes: 5 additions & 0 deletions virtio-bindings/include/linux/if_ether.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR Apache-2.0) */

#pragma once

#define ETH_ALEN 6 /* needed by virtio_net */
15 changes: 15 additions & 0 deletions virtio-bindings/include/linux/types.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: (BSD-3-Clause OR Apache-2.0) */

#pragma once

/* Built-ins avoid the need for <stdint.h> */
typedef __UINT8_TYPE__ __u8;
typedef __UINT16_TYPE__ __u16;
typedef __UINT32_TYPE__ __u32;
typedef __UINT64_TYPE__ __u64;

typedef __u16 __le16;
typedef __u32 __le32;
typedef __u64 __le64;

#define __bitwise /* ignore */
5 changes: 5 additions & 0 deletions virtio-bindings/include/linux/version.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#define LINUX_VERSION_CODE 396288
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c)))
#define LINUX_VERSION_MAJOR 6
#define LINUX_VERSION_PATCHLEVEL 12
#define LINUX_VERSION_SUBLEVEL 0
Loading

0 comments on commit 31f7c73

Please sign in to comment.