From a7398cc9eb40ee89ae46d0f72b726fbd93fa4ea7 Mon Sep 17 00:00:00 2001 From: hhang <169027046+xu91102@users.noreply.github.com> Date: Sat, 14 Nov 2026 10:31:26 +0000 Subject: [PATCH] fix(elf): preserve compiler metadata after an empty kernel builder Squashed from #9950 and rebased onto main. Fixes #9942 Supersedes #9950 Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> --- docs/guides/native/kernel-build-config.md | 5 ++-- src/native/elf/KernelBuildConfig.ts | 4 ++-- tests/acceptance/kernelBuildConfigCli.test.ts | 23 +++++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/docs/guides/native/kernel-build-config.md b/docs/guides/native/kernel-build-config.md index 971de720a5..c010dc65c5 100644 --- a/docs/guides/native/kernel-build-config.md +++ b/docs/guides/native/kernel-build-config.md @@ -31,8 +31,9 @@ otherwise. `banner` is the first printable string that starts with `Linux version ` followed by a version token beginning with a digit and ends at a NUL or newline within 512 bytes. `version` is that token. `builder` is the first -parenthesized group (`user@host`). `compiler` is the second group up to the -last `) #`, so nested parentheses in compiler strings are kept. Since Linux +parenthesized group (`user@host`) and is null when that group is empty. +`compiler` is the second group up to the last `) #`, so nested parentheses in +compiler strings are kept. Since Linux 6.1, kbuild also leaves the weak banner of `init/version.o`. It is compiled before the build number and timestamp exist, so unless `KBUILD_BUILD_VERSION` and `KBUILD_BUILD_TIMESTAMP` are both set, it has a space right after `#` or at diff --git a/src/native/elf/KernelBuildConfig.ts b/src/native/elf/KernelBuildConfig.ts index 39c81d8f97..cd735b964f 100644 --- a/src/native/elf/KernelBuildConfig.ts +++ b/src/native/elf/KernelBuildConfig.ts @@ -530,8 +530,8 @@ const parseBannerText = ( let compiler: string | null = null; if (rest.startsWith(" (")) { const close = rest.indexOf(")"); - if (close > 2) { - builder = rest.slice(2, close); + if (close >= 2) { + builder = rest.slice(2, close) || null; rest = rest.slice(close + 1); } } diff --git a/tests/acceptance/kernelBuildConfigCli.test.ts b/tests/acceptance/kernelBuildConfigCli.test.ts index 739f3e524d..1894d3663c 100644 --- a/tests/acceptance/kernelBuildConfigCli.test.ts +++ b/tests/acceptance/kernelBuildConfigCli.test.ts @@ -40,6 +40,29 @@ cliTest( }, ); +cliTest( + "keeps the compiler group separate from an empty builder group", + async ({ cli }) => { + const root = await createTestTempDirectory("rea-kernel-banner-cli-"); + const path = join(root, "banner.bin"); + const text = "Linux version 6.1.0 () (gcc 12.2.0) #1 SMP"; + await writeFile(path, `${text}\0`); + const result = await cli.run({ + arguments: ["inspect-kernel-build-config", path, "--json"], + }); + expect(result.exitCode, result.stderr).toBe(0); + const evidence = contract.outputSchema.parse(result.json); + expect(evidence.normalized_result.banner).toMatchObject({ + status: "present", + text, + version: "6.1.0", + builder: null, + compiler: "gcc 12.2.0", + location: { space: "file", offset: 0, bytes: text.length }, + }); + }, +); + cliTest( "resolves a relative bzImage path and pages its inflated config", async ({ cli }) => {