Skip to contentSkip to conversation

kbuild: rust: preserve unreachable traps with inline helpers

PATCH Applied Miguel Ojeda 5 participants 10 messages Aug 16 – Aug 24

Subsystems (3)
lore
Share & tools
lore

Miguel Ojeda

author maintainer
#0 lore raw

PATCH 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.

Assisted-by LLM
Cc 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")

---
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
index 902f3f3d54b7..85b84d895781 100644
@@ -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
index fbe0accc51a3..f871d94f6af2 100644
@@ -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
index 911745743246..0b3b81f4a652 100644
@@ -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

author maintainer
#1 lore raw

PATCH 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+0x1dfd

The 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.

Assisted-by LLM
Cc 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")

---
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
index f871d94f6af2..3afaad4a4a3a 100644
@@ -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
index 0b3b81f4a652..0b71f759eb5c 100644
@@ -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

reviewer Acked-by on v1 2/2
#2 lore raw

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 hit objtool errors such as:

    vmlinux.o: error: objtool: _R..._3Gsp4boot+0xd6a:
    can't find jump dest instruction at .text._R..._3Gsp4boot+0x1dfd

The 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.

Quoted 10 lines
Assisted-by LLM
Cc 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")
Quoted 4 lines
---
 rust/Makefile          | 2 +-
 scripts/Makefile.build | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
#3 lore raw

Re: [PATCH 2/2] kbuild: rust: keep Rust objects out of Clang LTO with inline helpers

In reply to Miguel Ojeda
Quoted 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
#4 lore raw

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

reviewer Acked-by on v1 1/2
#5 lore raw
In reply to Miguel Ojeda
Quoted 23 lines · Miguel Ojeda

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.

Quoted 10 lines
Assisted-by LLM
Cc 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")
Quoted 5 lines
---
 Makefile               | 10 ++++++++++
 rust/Makefile          |  3 ++-
 scripts/Makefile.build |  3 ++-
 3 files changed, 14 insertions(+), 2 deletions(-)
#6 lore raw
In reply to Miguel Ojeda
Quoted 15 lines · Miguel Ojeda

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.

Quoted 7 lines

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.

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
reviewer
#7 lore raw
Quoted 21 lines · Josh Poimboeuf
Quoted 20 lines · Miguel Ojeda

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.

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

#8 lore raw
In reply to Josh Poimboeuf
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

Applied
#9 lore raw
In reply to Miguel Ojeda
Quoted 24 lines · Miguel Ojeda

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.

Quoted 10 lines
Assisted-by LLM
Cc 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")

Applied series to rust-fixes -- thanks everyone!

Cheers,
Miguel