kbuild: rust: preserve unreachable traps with inline helpers
Subsystems (3)
Miguel Ojeda
maintainerPATCH 1/2 kbuild: rust: preserve unreachable traps with inline helpers
When CONFIG_RUST_INLINE_HELPERS is enabled, it is possible to hit objtool warnings like:
vmlinux.o: warning: objtool: _R..._4cmdq12CommandToGsp4init() falls through to next function _R..._4core5array4iter8IntoIterRShKj3_EEEBa_()
rustc normally emits traps for unreachable paths. However, under CONFIG_RUST_INLINE_HELPERS=y, rustc emits LLVM bitcode and Clang performs final code generation after the helper bitcode is linked, but Clang does not trap unreachable IR by default.
In turn, this means objtool follows compiler-generated impossible Rust enum paths through alignment padding into the next function, resulting in fallthrough warnings.
Thus pass the LLVM trap-unreachable option to the final Clang invocation and suppress traps immediately after noreturn calls, which objtool already recognizes as dead ends. The combination of both flags makes it match rustc's behavior.
Rust 1.85.0 (the minimum supported one) supports LLVM >= 18, and both flags are available in LLVM 18.
---
I pinged Gary offline to ask if he had to deal with these unreachable cases back when he sent the patch, and he didn't. So it looks like we simply did not hit the case until now.
Makefile | 10 ++++++++++ rust/Makefile | 3 ++- scripts/Makefile.build | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-)
Makefile +10 -0
@@ -1083,6 +1083,16 @@ endif export CC_FLAGS_SCS endif +ifdef CONFIG_RUST_INLINE_HELPERS +# `rustc` normally emits traps for unreachable paths during code generation. +# With inline helpers, Clang performs code generation from the linked bitcode +# instead, so request the same behavior explicitly. Otherwise `objtool` may +# follow an impossible Rust path into the next function. +CC_FLAGS_RUST_INLINE_HELPERS := -mllvm -trap-unreachable \ + -mllvm -no-trap-after-noreturn +export CC_FLAGS_RUST_INLINE_HELPERS +endif + ifdef CONFIG_LTO_CLANG ifdef CONFIG_LTO_CLANG_FULL CC_FLAGS_LTO := -flto
rust/Makefile +2 -1
@@ -644,7 +644,8 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L -Zunstable-options \ $(if $(link_helper),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ $(obj)/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ - $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ + $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) \ + $(CC_FLAGS_RUST_INLINE_HELPERS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ $(cmd_ld_single)) \ $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) \ $(cmd_objtool)
scripts/Makefile.build +2 -1
@@ -346,7 +346,8 @@ quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if $(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \ $(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ $(objtree)/rust/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ - $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ + $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) \ + $(CC_FLAGS_RUST_INLINE_HELPERS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ $(cmd_ld_single)) \ $(cmd_objtool)
base-commit: 47f27155f17498fccb1f222f79089642337498a9 --
2.55.0
Miguel Ojeda
maintainerPATCH 2/2 kbuild: rust: keep Rust objects out of Clang LTO with inline helpers
Under CONFIG_LTO_CLANG + CONFIG_RUST_INLINE_HELPERS, one may hit objtool errors such as:
vmlinux.o: error: objtool: _R..._3Gsp4boot+0xd6a:
can't find jump dest instruction at .text._R..._3Gsp4boot+0x1dfdThe reason is that in such builds, the Clang invocation that compiles the combined Rust plus helpers bitcode emits LLVM bitcode (again) -- the final code generation happens in the linker's LTO step, which the -mllvm trap options passed to Clang do not reach.
This, in turn, means that unreachable traps are missing, and the impossible paths do not merely fallthrough to the next symbol, but past the end of their own section, since LTO builds place each function in its own section.
Thus filter CC_FLAGS_LTO out of the Clang invocation, so that it always emits machine code directly, with the traps in place.
---
This is like the previous patch, but for the LTO case.
Another possible alternative that Gary suggested is to turn the flags on globally for C, whether only when inline helpers are enabled or in all cases, but it may be intended that in C we expect not to generate the unreachables.
Even if we do that later, we may want to still land this as the minimal fix for backporting, since it only affects the experimental option and only the Rust side.
rust/Makefile | 2 +- scripts/Makefile.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
rust/Makefile +1 -1
@@ -644,7 +644,7 @@ quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L -Zunstable-options \ $(if $(link_helper),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ $(obj)/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ - $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) \ + $(CC) $(CLANG_FLAGS) $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS)) \ $(CC_FLAGS_RUST_INLINE_HELPERS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ $(cmd_ld_single)) \ $(if $(rustc_objcopy),;$(OBJCOPY) $(rustc_objcopy) $@) \
scripts/Makefile.build +1 -1
@@ -346,7 +346,7 @@ quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@ cmd_rustc_o_rs = $(rust_common_cmd) --emit=$(if $(CONFIG_RUST_INLINE_HELPERS),llvm-bc=$(patsubst %.o,%.bc,$@),obj=$@) $< \ $(if $(CONFIG_RUST_INLINE_HELPERS),;$(LLVM_LINK) --internalize --suppress-warnings $(patsubst %.o,%.bc,$@) \ $(objtree)/rust/helpers/helpers$(if $(part-of-module),_module).bc -o $(patsubst %.o,%.m.bc,$@); \ - $(CC) $(CLANG_FLAGS) $(KBUILD_CFLAGS) \ + $(CC) $(CLANG_FLAGS) $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS)) \ $(CC_FLAGS_RUST_INLINE_HELPERS) -Wno-override-module -c $(patsubst %.o,%.m.bc,$@) -o $@ \ $(cmd_ld_single)) \ $(cmd_objtool)
--
2.55.0
Re: [PATCH 2/2] kbuild: rust: keep Rust objects out of Clang LTO with inline helpers
Quoted 19 lines · Miguel Ojeda
Under
CONFIG_LTO_CLANG+CONFIG_RUST_INLINE_HELPERS, one may hitobjtoolerrors such as:vmlinux.o: error: objtool: _R..._3Gsp4boot+0xd6a: can't find jump dest instruction at .text._R..._3Gsp4boot+0x1dfdThe reason is that in such builds, the Clang invocation that compiles the combined Rust plus helpers bitcode emits LLVM bitcode (again) -- the final code generation happens in the linker's LTO step, which the
-mllvmtrap options passed to Clang do not reach.This, in turn, means that unreachable traps are missing, and the impossible paths do not merely fallthrough to the next symbol, but past the end of their own section, since LTO builds place each function in its own section.
Thus filter
CC_FLAGS_LTOout of the Clang invocation, so that it always emits machine code directly, with the traps in place.
Quoted 10 lines
Assisted-by LLMCc Gary Guo <[email protected]>Cc Boqun Feng <[email protected]>Cc Alice Ryhl <[email protected]>Cc Matthew Maurer <[email protected]>Cc Josh Poimboeuf <[email protected]>Cc Peter Zijlstra <[email protected]>Fixes 3a2486cc1da5 ("kbuild: rust: provide an option to inline C helpers into Rust")Signed-off-by Miguel Ojeda <[email protected]>
Quoted 4 lines
--- rust/Makefile | 2 +- scripts/Makefile.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
Nathan Chancellor
maintainerRe: [PATCH 2/2] kbuild: rust: keep Rust objects out of Clang LTO with inline helpers
In reply to Miguel OjedaQuoted 4 lines · Miguel Ojeda
Another possible alternative that Gary suggested is to turn the flags on globally for C, whether only when inline helpers are enabled or in all cases, but it may be intended that in C we expect not to generate the
unreachables.
I am not sure we can use '-mllvm -trap-unreachable' for C code, as that would mess with manually inserted calls to __builtin_unreachable(). It has come up before:
https://lore.kernel.org/CAHk-=wgPCbZv0JgqoNWMOO+p=N772YW16xYk_pmb1GU7aeuPFA@mail.gmail.com/
It is possible the '-no-trap-after-noreturn' avoids that particular issue but I still think the "compiler inserted" vs. "user inserted" distinction that Linus comments on is likely still potentially relevant.
Signature · 2 lines
Cheers, Nathan
Re: [PATCH 2/2] kbuild: rust: keep Rust objects out of Clang LTO with inline helpers
Quoted 10 lines · Nathan Chancellor
I am not sure we can use '-mllvm -trap-unreachable' for C code, as that would mess with manually inserted calls to
__builtin_unreachable(). It has come up before:https://lore.kernel.org/CAHk-=wgPCbZv0JgqoNWMOO+p=N772YW16xYk_pmb1GU7aeuPFA@mail.gmail.com/
It is possible the '-no-trap-after-noreturn' avoids that particular issue but I still think the "compiler inserted" vs. "user inserted" distinction that Linus comments on is likely still potentially relevant.
Thanks Nathan -- yeah, that is the kind of "intended" thing I was wondering about in my note above... It would be nice if there could be a distinction between the developer-provided ones and the ones that get inserted for other reasons.
Ok, I picked these up to fix the immediate issue, since they only affect the experimental mode and only the Rust side. If there is a better approach available in the future, then great.
Cheers,
Miguel
Quoted 23 lines · Miguel Ojeda
When
CONFIG_RUST_INLINE_HELPERSis enabled, it is possible to hitobjtoolwarnings like:vmlinux.o: warning: objtool: _R..._4cmdq12CommandToGsp4init() falls through to next function _R..._4core5array4iter8IntoIterRShKj3_EEEBa_()
rustcnormally emits traps for unreachable paths. However, underCONFIG_RUST_INLINE_HELPERS=y,rustcemits LLVM bitcode and Clang performs final code generation after the helper bitcode is linked, but Clang does not trap unreachable IR by default.In turn, this means
objtoolfollows compiler-generated impossible Rustenumpaths through alignment padding into the next function, resulting in fallthrough warnings.Thus pass the LLVM
trap-unreachableoption to the final Clang invocation and suppress traps immediately afternoreturncalls, whichobjtoolalready recognizes as dead ends. The combination of both flags makes it matchrustc's behavior.Rust 1.85.0 (the minimum supported one) supports LLVM >= 18, and both flags are available in LLVM 18.
Quoted 10 lines
Assisted-by LLMCc Gary Guo <[email protected]>Cc Boqun Feng <[email protected]>Cc Alice Ryhl <[email protected]>Cc Matthew Maurer <[email protected]>Cc Josh Poimboeuf <[email protected]>Cc Peter Zijlstra <[email protected]>Fixes 3a2486cc1da5 ("kbuild: rust: provide an option to inline C helpers into Rust")Signed-off-by Miguel Ojeda <[email protected]>
Quoted 5 lines
--- Makefile | 10 ++++++++++ rust/Makefile | 3 ++- scripts/Makefile.build | 3 ++- 3 files changed, 14 insertions(+), 2 deletions(-)
Quoted 15 lines · Miguel Ojeda
When
CONFIG_RUST_INLINE_HELPERSis enabled, it is possible to hitobjtoolwarnings like:vmlinux.o: warning: objtool: _R..._4cmdq12CommandToGsp4init() falls through to next function _R..._4core5array4iter8IntoIterRShKj3_EEEBa_()
rustcnormally emits traps for unreachable paths. However, underCONFIG_RUST_INLINE_HELPERS=y,rustcemits LLVM bitcode and Clang performs final code generation after the helper bitcode is linked, but Clang does not trap unreachable IR by default.In turn, this means
objtoolfollows compiler-generated impossible Rustenumpaths through alignment padding into the next function, resulting in fallthrough warnings.
Quoted 7 lines
Thus pass the LLVM
trap-unreachableoption to the final Clang invocation and suppress traps immediately afternoreturncalls, whichobjtoolalready recognizes as dead ends. The combination of both flags makes it matchrustc's behavior.Rust 1.85.0 (the minimum supported one) supports LLVM >= 18, and both flags are available in LLVM 18.
FWIW, I've been working on some patches that should hopefully let us get rid of all the objtool hard-coded noreturn lists altogether. I'll try to post those soon.
Signature · 1 line
Josh
Gary Guo
reviewerQuoted 21 lines · Josh Poimboeuf
Quoted 20 lines · Miguel Ojeda
When
CONFIG_RUST_INLINE_HELPERSis enabled, it is possible to hitobjtoolwarnings like:vmlinux.o: warning: objtool: _R..._4cmdq12CommandToGsp4init() falls through to next function _R..._4core5array4iter8IntoIterRShKj3_EEEBa_()
rustcnormally emits traps for unreachable paths. However, underCONFIG_RUST_INLINE_HELPERS=y,rustcemits LLVM bitcode and Clang performs final code generation after the helper bitcode is linked, but Clang does not trap unreachable IR by default.In turn, this means
objtoolfollows compiler-generated impossible Rustenumpaths through alignment padding into the next function, resulting in fallthrough warnings.Thus pass the LLVM
trap-unreachableoption to the final Clang invocation and suppress traps immediately afternoreturncalls, whichobjtoolalready recognizes as dead ends. The combination of both flags makes it matchrustc's behavior.
Quoted 6 lines
Quoted 2 lines
Rust 1.85.0 (the minimum supported one) supports LLVM >= 18, and both flags are available in LLVM 18.
FWIW, I've been working on some patches that should hopefully let us get rid of all the objtool hard-coded noreturn lists altogether. I'll try to post those soon.
That'll be nice. I once prototyped a generator for such list by using dwarf debug info, but obviously that cannot be part of objtool because objtool operates independently from dwarf.
Looking forward to your approach and how you solve this. Please keep me (and the rust-for-linux list) in cc when you do.
Thanks,
Gary
Quoted 4 lines · Josh Poimboeuf
FWIW, I've been working on some patches that should hopefully let us get rid of all the objtool hard-coded noreturn lists altogether. I'll try to post those soon.
Echoing Gary's +1: a good solution for this would be very welcome. I have added a link to your message in:
https://github.com/Rust-for-Linux/linux/issues/355
since it is one more possible alternative, which means we wouldn't need upstream Rust to provide that noreturn-symbol list in such a case.
Thanks!
Cheers,
Miguel
Miguel Ojeda
AppliedQuoted 24 lines · Miguel Ojeda
When
CONFIG_RUST_INLINE_HELPERSis enabled, it is possible to hitobjtoolwarnings like:vmlinux.o: warning: objtool: _R..._4cmdq12CommandToGsp4init() falls through to next function _R..._4core5array4iter8IntoIterRShKj3_EEEBa_()
rustcnormally emits traps for unreachable paths. However, underCONFIG_RUST_INLINE_HELPERS=y,rustcemits LLVM bitcode and Clang performs final code generation after the helper bitcode is linked, but Clang does not trap unreachable IR by default.In turn, this means
objtoolfollows compiler-generated impossible Rustenumpaths through alignment padding into the next function, resulting in fallthrough warnings.Thus pass the LLVM
trap-unreachableoption to the final Clang invocation and suppress traps immediately afternoreturncalls, whichobjtoolalready recognizes as dead ends. The combination of both flags makes it matchrustc's behavior.Rust 1.85.0 (the minimum supported one) supports LLVM >= 18, and both flags are available in LLVM 18.
Quoted 10 lines
Assisted-by LLMCc Gary Guo <[email protected]>Cc Boqun Feng <[email protected]>Cc Alice Ryhl <[email protected]>Cc Matthew Maurer <[email protected]>Cc Josh Poimboeuf <[email protected]>Cc Peter Zijlstra <[email protected]>Fixes 3a2486cc1da5 ("kbuild: rust: provide an option to inline C helpers into Rust")Signed-off-by Miguel Ojeda <[email protected]>
Applied series to rust-fixes -- thanks everyone!
Cheers,
Miguel