Skip to content

devices: build init binary as part of build script#578

Merged
slp merged 3 commits intocontainers:mainfrom
d-e-s-o:topic/build-init.c
Mar 13, 2026
Merged

devices: build init binary as part of build script#578
slp merged 3 commits intocontainers:mainfrom
d-e-s-o:topic/build-init.c

Conversation

@d-e-s-o
Copy link
Contributor

@d-e-s-o d-e-s-o commented Mar 10, 2026

Build the default init binary automatically from a Rust build script, instead of from the Makefile. Doing so is a stepping stone to consuming the crate from Cargo. We do build the init binary unconditionally compared to before to not duplicate the logic overly much. But I would think that's not an issue. We keep supporting cross-compilation from MacOS when using the Makefile as before.

This PR is a necessary first step towards the changes discussed here.

@d-e-s-o d-e-s-o force-pushed the topic/build-init.c branch from 1f75a2f to a4aae9c Compare March 10, 2026 20:00
@d-e-s-o d-e-s-o changed the title build init binary as part of build script devices: build init binary as part of build script Mar 10, 2026
@ggoodman
Copy link
Contributor

As an idea, what if the init binary was its own crate and libkrun's rust api accepted a memory range for the static init binary. The c API would consume the new init crate and pass that as the default init memory range.

In this way the c library maintains compatibility while the first release of the rust API doesn't bake in an assumed init.

@d-e-s-o
Copy link
Contributor Author

d-e-s-o commented Mar 11, 2026

As an idea, what if the init binary was its own crate and libkrun's rust api accepted a memory range for the static init binary. The c API would consume the new init crate and pass that as the default init memory range.

Can you sketch out the idea a bit more? Do I assume correctly that the init crate would just expose the C binary as a static blob and not be a Rust rewrite?

Also, can you elaborate more on how to overwrite the default? Would the idea be that users invoke some C API with a different memory range, thereby swapping out the default init? If so, it would seem to me that this could be done with the approach at hand as well as a Rust crate, right?

@ggoodman
Copy link
Contributor

Let's say the rust api had a method like with_init(&init_binary), that would mean that the main libkrun crate wouldn't need to bake in a binary at all. It would fail to start the VM if none were provided.

For users who don't want to supply their own binary, then we can have an init crate whose main (only) export is a value / reference suitable to being passed to the with_init() method.

It could even be the default to wire up the init binary with a feature-flag opt-out.

So we get:

  1. A built-in init binary identical to what we have now
  2. A libkrun crate that has a way to be used without the built-in binary (because the consumer has their own)
  3. A way to provide transparent backwards-compatibility in the c api (we do the wiring for that interface)

@slp
Copy link
Collaborator

slp commented Mar 12, 2026

@d-e-s-o I like this approach, thanks. It has also highlighted we are no longer building init for SNP here, which makes me wonder wether we should move the init variant with CoCo to a different repo. WDYT @tylerfanelli @jakecorrenti ?

@ggoodman You can already do that by setting KRUN_INIT_BINARY_PATH. We could have a another flag to completely disable the embedded init, but that would be outside the scope of this PR.

@slp
Copy link
Collaborator

slp commented Mar 12, 2026

/gemini review

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 moves the compilation of the init binary from the Makefile to a Rust build script (build.rs). This is a good step towards making the project more cargo-friendly. However, I've found a critical issue where the new build script omits the conditional compilation logic required for SEV and TDX builds, which will result in a broken init binary for those configurations. Additionally, there are some issues in the Makefile regarding a now-undefined variable and incorrect dependencies that should be addressed for build correctness and clarity.

Comment on lines +20 to +23
let mut init_cc_flags = vec!["-O2", "-static", "-Wall"];
if std::env::var_os("TIMESYNC").as_deref() == Some(OsStr::new("1")) {
init_cc_flags.push("-D__TIMESYNC__");
}

Choose a reason for hiding this comment

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

critical

This build script is missing the conditional compilation logic for SEV and TDX builds that was present in the Makefile. The init binary requires different C defines, additional source files, and specific linker flags when built with SEV or TDX support.

Without this logic, the init binary will be incorrect for SEV and TDX builds, leading to runtime failures. You should check for Cargo features like CARGO_FEATURE_AMD_SEV and CARGO_FEATURE_TDX and adjust the compilation command accordingly.

For example, for SEV, you need to:

  1. Add -DSEV=1 to init_cc_flags.
  2. Add init/tee/snp_attest.c and the init/tee/kbs/*.c files to the list of source files to compile.
  3. Add flags like -lcurl, -lssl, etc., to the linker.
  4. Add cargo:rerun-if-changed for all the new source files and headers.

A similar logic is needed for TDX.

Copy link
Contributor Author

@d-e-s-o d-e-s-o Mar 12, 2026

Choose a reason for hiding this comment

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

This comment seems wrong. As per my understanding this was dead code, see reasoning in a68a626, but let me know if I missed something.

d-e-s-o added 3 commits March 12, 2026 07:34
We shouldn't stringify paths, because not all paths are valid UTF-8
strings, meaning we could fail operations spuriously. Work with proper
Rust paths to not run into such issues.

Signed-off-by: Daniel Müller <deso@posteo.net>
When the SEV or TDX environment variables are 1, we set BUILD_INIT to 0.
Unless BUILD_INIT is 1, we don't actually build the init binary, which
is the only part of the Makefile that uses INIT_DEFS and INIT_SRC. As
such, there seems to be no point in setting both INIT_DEFS and INIT_SRC
from the code paths used when SEV or TDX are 1.

Remove these assignments. That in turn also allows us to remove the
KBS_INIT_SRC, KBS_LD_FLAGS, and SNP_INIT_SRC variables. TDX_INIT_SRC
seems to have been unused all along.

Signed-off-by: Daniel Müller <deso@posteo.net>
Build the default init binary automatically from a Rust build script,
instead of from the Makefile. Doing so is a stepping stone to consuming
the crate from Cargo. We do build the init binary unconditionally
compared to before to not duplicate the logic overly much. But I would
think that's not an issue. We keep supporting cross-compilation from
MacOS when using the Makefile as before.

Signed-off-by: Daniel Müller <deso@posteo.net>
@d-e-s-o d-e-s-o force-pushed the topic/build-init.c branch from a4aae9c to 205dada Compare March 12, 2026 14:34
cd $(DESTDIR)$(PREFIX)/$(LIBDIR_$(OS))/ ; ln -sf $(KRUN_BINARY_$(OS)) $(KRUN_SONAME_$(OS)) ; ln -sf $(KRUN_SONAME_$(OS)) $(KRUN_BASE_$(OS))

clean:
rm -f $(INIT_BINARY)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The downside of building in the build script and us storing stuff in the source tree is that cargo clean won't remove the binary. I think we could:

  1. live with that; I don't think it's the end of the world, but not great either
  2. just keep removing init/init here from the Makefile (it's a bit odd to have two different components create/remove the file)
  3. the proper way would be to move the created init binary into OUT_DIR, which I think is probably the best approach. It would just mean adjusting KRUN_INIT_BINARY_PATH from the build script.

Any preferences?

Copy link
Member

Choose a reason for hiding this comment

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

I prefer doing it the right way, so in my opinion we should do option 3.

@tylerfanelli
Copy link
Member

tylerfanelli commented Mar 12, 2026

It has also highlighted we are no longer building init for SNP here, which makes me wonder wether we should move the init variant with CoCo to a different repo. WDYT @tylerfanelli @jakecorrenti ?

I'm not opposed. Especially because the nitro init binary is now different than the standard CoCo init binary. It seems this option would give us the flexibility to add whichever init we want.

@jakecorrenti
Copy link
Member

I'm also not opposed to moving the CoCo init source code to another repository. IMO it would make the build process much easier allowing us to avoid weird scripts.

@slp
Copy link
Collaborator

slp commented Mar 13, 2026

@tylerfanelli @jakecorrenti thank you guys, let's then get this one merged then. Could you please take care of moving CoCo's init to another repo?

Copy link
Collaborator

@slp slp left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@slp slp merged commit 53920d2 into containers:main Mar 13, 2026
11 checks passed
@jakecorrenti
Copy link
Member

@tylerfanelli @jakecorrenti thank you guys, let's then get this one merged then. Could you please take care of moving CoCo's init to another repo?

Sure I can take care of it. Just to confirm -- you don't want it in something like libkrunfw, but something entirely different?

@d-e-s-o d-e-s-o deleted the topic/build-init.c branch March 13, 2026 13:26
d-e-s-o added a commit to d-e-s-o/libkrun that referenced this pull request Mar 13, 2026
As per previous discussion [0], adjust the build script to build the
init binary in Cargo's OUT_DIR, which is meant precisely for use cases
like this. This way, we don't leak a random binary into the source tree,
where it would have to be removed manually.

[0] containers#578 (comment)

Signed-off-by: Daniel Müller <deso@posteo.net>
d-e-s-o added a commit to d-e-s-o/libkrun that referenced this pull request Mar 13, 2026
As per previous discussion [0], adjust the build script to build the
init binary in Cargo's OUT_DIR, which is meant precisely for use cases
like this. This way, we don't leak a random binary into the source tree,
where it would have to be removed manually.

[0] containers#578 (comment)

Signed-off-by: Daniel Müller <deso@posteo.net>
@slp
Copy link
Collaborator

slp commented Mar 16, 2026

@tylerfanelli @jakecorrenti thank you guys, let's then get this one merged then. Could you please take care of moving CoCo's init to another repo?

Sure I can take care of it. Just to confirm -- you don't want it in something like libkrunfw, but something entirely different?

libkrunfw could be a good place for it, indeed. We could also add the scripts to generate the initramfs. Thanks!

slp pushed a commit that referenced this pull request Mar 16, 2026
As per previous discussion [0], adjust the build script to build the
init binary in Cargo's OUT_DIR, which is meant precisely for use cases
like this. This way, we don't leak a random binary into the source tree,
where it would have to be removed manually.

[0] #578 (comment)

Signed-off-by: Daniel Müller <deso@posteo.net>
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.

5 participants