Skip to content

Comments

feat: allow overriding embedded binary paths via env variables#553

Open
rrauch wants to merge 3 commits intocontainers:mainfrom
rrauch:include-init-binary-from-env
Open

feat: allow overriding embedded binary paths via env variables#553
rrauch wants to merge 3 commits intocontainers:mainfrom
rrauch:include-init-binary-from-env

Conversation

@rrauch
Copy link

@rrauch rrauch commented Feb 18, 2026

This PR allows overriding the currently hard-coded paths of INIT_BINARY (and also EDK2_BINARY) at build time.

I've had an issue when using the devices crate from outside the libkrun tree:

Currently, it's perfectly possible to include the crate in my own project (using the git crate location type in my Cargo.toml), but the build will fail because the init binary included in virtio::fs::<os>::passthrough cannot be found.

To fix this, I've replaced

static INIT_BINARY: &[u8] = include_bytes!("../../../../../../init/init");

with

static INIT_BINARY: &[u8] = include_bytes!(env!("INIT_BINARY_PATH"));

Now the devices crate can be built even when used out-of-tree. A nice side-effect is that this also allows the use of a custom init when desired.

If no INIT_BINARY_PATH env variable is set, it falls back to the currently used path, so this PR should remain backwards compatible.

I've noticed that EDK2_BINARY (in vmm) follows a similar approach, so I have updated that too - even though I am not currently using aarch64. I have not tested this myself, but believe it should be fine.

@gemini-code-assist
Copy link

Summary of Changes

Hello @rrauch, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the build process by introducing environment variable overrides for embedded binary paths. This change primarily addresses the challenge of using the devices crate in external projects, allowing it to be built out-of-tree. It also provides greater flexibility for developers to specify custom init and EDK2 binaries, while maintaining backward compatibility with existing build configurations.

Highlights

  • Environment Variable Overrides: Enabled overriding the INIT_BINARY path at build time using the INIT_BINARY_PATH environment variable, and similarly for EDK2_BINARY via EDK2_BINARY_PATH for aarch64 builds.
  • Out-of-Tree Crate Usage: Resolved issues preventing the devices crate from being built and used outside the libkrun tree, improving modularity and reusability.
  • Custom Binary Flexibility: Introduced flexibility to use custom init and EDK2 binaries by allowing their paths to be specified externally.
  • Backward Compatibility: Ensured backward compatibility by providing fallback paths if the new environment variables are not explicitly set, maintaining existing build configurations.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/devices/Cargo.toml
    • Added build = "build.rs" to enable custom build script execution.
  • src/devices/build.rs
    • Introduced a new build script to define the INIT_BINARY_PATH environment variable.
    • Configured INIT_BINARY_PATH to use an environment variable if set, otherwise falling back to a relative path.
    • Added rerun-if-changed directive for the init_binary_path.
  • src/devices/src/virtio/fs/linux/passthrough.rs
    • Modified INIT_BINARY to load bytes from the path specified by env!("INIT_BINARY_PATH").
  • src/devices/src/virtio/fs/macos/passthrough.rs
    • Modified INIT_BINARY to load bytes from the path specified by env!("INIT_BINARY_PATH").
  • src/vmm/Cargo.toml
    • Added build = "build.rs" to enable custom build script execution.
  • src/vmm/build.rs
    • Introduced a new build script to define the EDK2_BINARY_PATH environment variable for aarch64 targets.
    • Configured EDK2_BINARY_PATH to use an environment variable if set, otherwise falling back to a relative path.
    • Added rerun-if-changed directive for the edk2_binary_path.
  • src/vmm/src/builder.rs
    • Modified EDK2_BINARY to load bytes from the path specified by env!("EDK2_BINARY_PATH").
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a mechanism to override hardcoded paths for embedded binaries (INIT_BINARY and EDK2_BINARY) using environment variables at build time. This is a valuable improvement for enabling out-of-tree builds. The implementation correctly uses build scripts to set the necessary environment variables for include_bytes!. My review includes suggestions to improve the robustness of the new build scripts by replacing unwrap() calls with expect() to provide more descriptive error messages in case of build failures.

Copy link
Collaborator

@mtjhrc mtjhrc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is sound and this could be quite usefull, I would rename the environment variable to be safe.
Also please make sure to sign off the commits (the "Signed-off-by:" footer, git commit --signoff)

)
});
println!("cargo:rustc-env=INIT_BINARY_PATH={init_binary_path}");
println!("cargo:rerun-if-changed={init_binary_path}");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be println!("cargo:rerun-if-env-changed=INIT_BINARY_PATH")?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-env-changed:

As of 1.46, using env! and option_env! in source code will automatically detect changes and trigger rebuilds. rerun-if-env-changed is no longer needed for variables already referenced by these macros.

So that case is already covered by the env! macro.

println!("cargo:rerun-if-changed={init_binary_path}") handles the case where the init binary itself changed (e.g. recompiled).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually we should have both. In case the value of the env variable had changed, and in case someone updated the binary file of init.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. And I think both cases are already covered. See my previous comment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, It seems like include_bytes! seem to already handle that too (but I can't find it documented now)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find anything in the docs either. I found rust-lang/rust#24423 which states that the included file is tracked, but in the commit itself it says only the file is tracked and not its content - whatever that means in this context.

I would suggest to keep the cargo:rerun-if-changed - I can't see any harm here and it makes it explicit.
However, if you'd prefer to have it removed, I don't mind either.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken, the rerun-if-* stuff specifies when the build script should be rebuilt. It's necessary to include the check for the environment variable (there is no env! in the script) so that the changed path is noticed. rerun-if-changed is superfluous because the contents of the file has no influence on the build script itself; having it included is harmless (at worst the build script is rebuilt unnecessary) but we should understand what's happening exactly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mz-pdm Your comment is spot on! rerun-if-* is really only about when to re-run the build script, nothing else.
I believe your original suggestion is correct then; it should indeed be cargo:rerun-if-env-changed. I stand corrected.

I've tried it & it works reliably. Also the scenario where KRUN_INIT_BINARY_PATH was set to some value previously but has since become unset works correctly. I believe it might have been a bit "flaky" before.

I'll change it to cargo:rerun-if-env-changed then.

Adds `build.rs` to the `devices` and `vmm` crates. The `INIT_BINARY`
path in `devices` can now be overridden with `INIT_BINARY_PATH`, and
the `EDK2_BINARY` path in `vmm` with `EDK2_BINARY_PATH` respectively.
Both fall back to the original paths if not set.

Signed-off-by: Roland Rauch <roland@rauch.com>
@rrauch rrauch force-pushed the include-init-binary-from-env branch from bc31477 to 26e939d Compare February 19, 2026 12:23
To avoid any accidental inclusions, `INIT_BINARY_PATH` is renamed to
`KRUN_INIT_BINARY_PATH` and `EDK2_BINARY_PATH` to `KRUN_EDK2_BINARY_PATH`.

Signed-off-by: Roland Rauch <roland@rauch.com>
@ggoodman
Copy link
Contributor

I had experimented with requiring the embedding project to supply the init binary as a memory range. This was quite nice as in my use case the coupling was between the embedding program and the init. So in that design, libkrun was only responsible for the hand-off from the host to the guest init instead of also owning init. I think the coupling and complexity of the built-in init suffers because it has to handle every use-case.

@mtjhrc
Copy link
Collaborator

mtjhrc commented Feb 19, 2026

I had experimented with requiring the embedding project to supply the init binary as a memory range. This was quite nice as in my use case the coupling was between the embedding program and the init. So in that design, libkrun was only responsible for the hand-off from the host to the guest init instead of also owning init.

We will likely end up doing something like this for libkrun 2.0.
IMO we should eventually have an API for specifying any path and a memory range to inject into the fs. Then we could ship the init as some kind of extension for container runtime (or maybe as part of libkrunfw?).

This change correctly triggers a build.rs rerun if the KRUN_*_BINARY_PATH
environment variable has changed.

Signed-off-by: Roland Rauch <roland@rauch.com>
@rrauch
Copy link
Author

rrauch commented Feb 19, 2026

@ggoodman I am also using my own init binary currently. I am actually supplying it via a initrd as I am not too fond of the "virtual init-inode" approach libkrun is currently taking to inject the init binary via virtio-fs.

I had to make some small changes to vmm to allow the general initrd use as it is currently limited to the tee feature. I wasn't sure at first if it was going to work because of this limit, but in the end it was only a couple of lines and it seems to work just fine.

However, the devices crate still insists on an init binary. Hence this PR. In my use-case I am just feeding it a dummy binary to satisfy the compiler.

@rrauch rrauch requested a review from mtjhrc February 20, 2026 13:46
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 this pull request may close these issues.

4 participants