Skip to content

Commit

Permalink
Add -Zfunction-return={keep,thunk-extern} option
Browse files Browse the repository at this point in the history
This is intended to be used for Linux kernel RETHUNK builds.

With this commit (optionally backported to Rust 1.73.0), plus a
patched Linux kernel to pass the flag, I get a RETHUNK build with
Rust enabled that is `objtool`-warning-free and is able to boot in
QEMU and load a sample Rust kernel module.

Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
  • Loading branch information
ojeda committed Oct 18, 2023
1 parent e1de04a commit 6a0ae7d
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 4 deletions.
12 changes: 11 additions & 1 deletion compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rustc_codegen_ssa::traits::*;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::config::OptLevel;
use rustc_session::config::{FunctionReturn, OptLevel};
use rustc_span::symbol::sym;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::{FramePointer, SanitizerSet, StackProbeType, StackProtector};
Expand Down Expand Up @@ -118,6 +118,15 @@ pub fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attr
Some(llvm::CreateAttrStringValue(cx.llcx, "frame-pointer", attr_value))
}

fn function_return_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
let function_return_attr = match cx.sess().opts.unstable_opts.function_return {
FunctionReturn::Keep => return None,
FunctionReturn::ThunkExtern => AttributeKind::FnRetThunkExtern,
};

Some(function_return_attr.create_attr(cx.llcx))
}

/// Tell LLVM what instrument function to insert.
#[inline]
fn instrument_function_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> SmallVec<[&'ll Attribute; 4]> {
Expand Down Expand Up @@ -333,6 +342,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(

// FIXME: none of these three functions interact with source level attributes.
to_add.extend(frame_pointer_type_attr(cx));
to_add.extend(function_return_attr(cx));
to_add.extend(instrument_function_attr(cx));
to_add.extend(nojumptables_attr(cx));
to_add.extend(probestack_attr(cx));
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ pub enum AttributeKind {
AllocatedPointer = 38,
AllocAlign = 39,
SanitizeSafeStack = 40,
FnRetThunkExtern = 41,
}

/// LLVMIntPredicate
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rustc_data_structures::profiling::TimePassesFormat;
use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
use rustc_session::config::rustc_optgroups;
use rustc_session::config::DebugInfo;
use rustc_session::config::FunctionReturn;
use rustc_session::config::Input;
use rustc_session::config::InstrumentXRay;
use rustc_session::config::LinkSelfContained;
Expand Down Expand Up @@ -782,6 +783,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(flatten_format_args, false);
tracked!(force_unstable_if_unmarked, true);
tracked!(fuel, Some(("abc".to_string(), 99)));
tracked!(function_return, FunctionReturn::ThunkExtern);
tracked!(function_sections, Some(false));
tracked!(human_readable_cgu_names, true);
tracked!(incremental_ignore_spans, true);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ enum LLVMRustAttribute {
AllocatedPointer = 38,
AllocAlign = 39,
SanitizeSafeStack = 40,
FnRetThunkExtern = 41,
};

typedef struct OpaqueRustString *RustStringRef;
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
return Attribute::AllocAlign;
case SanitizeSafeStack:
return Attribute::SafeStack;
case FnRetThunkExtern:
return Attribute::FnRetThunkExtern;
}
report_fatal_error("bad AttributeKind");
}
Expand Down
18 changes: 15 additions & 3 deletions compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3171,9 +3171,9 @@ impl PpMode {
pub(crate) mod dep_tracking {
use super::{
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, DebugInfoCompression,
ErrorOutputType, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
LtoCli, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Polonius,
ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
ErrorOutputType, FunctionReturn, InstrumentCoverage, InstrumentXRay, LinkerPluginLto,
LocationDetail, LtoCli, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
Polonius, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath,
SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
};
use crate::lint;
Expand Down Expand Up @@ -3279,6 +3279,7 @@ pub(crate) mod dep_tracking {
LanguageIdentifier,
TraitSolver,
Polonius,
FunctionReturn,
);

impl<T1, T2> DepTrackingHash for (T1, T2)
Expand Down Expand Up @@ -3449,3 +3450,14 @@ impl Polonius {
matches!(self, Polonius::Next)
}
}

/// The different settings that the `-Zfunction-return` flag can have.
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
pub enum FunctionReturn {
/// Keep the function return unmodified.
#[default]
Keep,

/// Replace returns with jumps to thunk, without emitting the thunk.
ThunkExtern,
}
12 changes: 12 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ mod desc {
pub const parse_proc_macro_execution_strategy: &str =
"one of supported execution strategies (`same-thread`, or `cross-thread`)";
pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`";
pub const parse_function_return: &str = "`keep` or `thunk-extern`";
}

mod parse {
Expand Down Expand Up @@ -1284,6 +1285,15 @@ mod parse {
};
true
}

pub(crate) fn parse_function_return(slot: &mut FunctionReturn, v: Option<&str>) -> bool {
match v {
Some("keep") => *slot = FunctionReturn::Keep,
Some("thunk-extern") => *slot = FunctionReturn::ThunkExtern,
_ => return false,
}
true
}
}

options! {
Expand Down Expand Up @@ -1530,6 +1540,8 @@ options! {
"force all crates to be `rustc_private` unstable (default: no)"),
fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED],
"set the optimization fuel quota for a crate"),
function_return: FunctionReturn = (FunctionReturn::default(), parse_function_return, [TRACKED],
"replace returns with jumps to `__x86_return_thunk` (default: `keep`)"),
function_sections: Option<bool> = (None, parse_opt_bool, [TRACKED],
"whether each function should go in its own section"),
future_incompat_test: bool = (false, parse_bool, [UNTRACKED],
Expand Down
25 changes: 25 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/function-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# `function-return`

The tracking issue for this feature is: https://github.com/rust-lang/rust/issues/116853.

------------------------

Option `-Zfunction-return` controls how function returns are converted.

It is equivalent to [Clang]'s and [GCC]'s `-mfunction-return`. The Linux kernel
uses it for RETHUNK builds. For details, see [LLVM commit 2240d72f15f3] ("[X86]
initial -mfunction-return=thunk-extern support") which introduces the feature.

Supported values for this option are:

- `keep`: do not convert function returns.
- `thunk-extern`: convert function returns (`ret`) to jumps (`jmp`)
to an external symbol called `__x86_return_thunk`.

Like in Clang, GCC's values `thunk` and `thunk-inline` are not supported.

Only x86 is supported.

[Clang]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mfunction-return
[GCC]: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mfunction-return
[LLVM commit 2240d72f15f3]: https://github.com/llvm/llvm-project/commit/2240d72f15f3b7b9d9fb65450f9bf635fd310f6f
28 changes: 28 additions & 0 deletions tests/assembly/x86_64-function-return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Test that the function return is (not) converted into a jump to the thunk
// when the `-Zfunction-return={keep,thunk-extern}` flag is (not) set.

// revisions: unset keep thunk-extern keep-thunk-extern thunk-extern-keep
// assembly-output: emit-asm
// compile-flags: -O
// [keep] compile-flags: -Zfunction-return=keep
// [thunk-extern] compile-flags: -Zfunction-return=thunk-extern
// [keep-thunk-extern] compile-flags: -Zfunction-return=keep -Zfunction-return=thunk-extern
// [thunk-extern-keep] compile-flags: -Zfunction-return=thunk-extern -Zfunction-return=keep
// only-x86_64

#![crate_type = "lib"]

// CHECK-LABEL: foo:
#[no_mangle]
pub unsafe fn foo() {
// unset: ret
// unset-NOT: jmp __x86_return_thunk
// keep: ret
// keep-NOT: jmp __x86_return_thunk
// thunk-extern: jmp __x86_return_thunk
// thunk-extern-NOT: ret
// keep-thunk-extern: jmp __x86_return_thunk
// keep-thunk-extern-NOT: ret
// thunk-extern-keep: ret
// thunk-extern-keep-NOT: jmp __x86_return_thunk
}
28 changes: 28 additions & 0 deletions tests/codegen/function-return.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Test that the `fn_ret_thunk_extern` function attribute is (not) emitted when
// the `-Zfunction-return={keep,thunk-extern}` flag is (not) set.

// revisions: unset keep thunk-extern keep-thunk-extern thunk-extern-keep
// needs-llvm-components: x86
// compile-flags: --target x86_64-unknown-linux-gnu
// [keep] compile-flags: -Zfunction-return=keep
// [thunk-extern] compile-flags: -Zfunction-return=thunk-extern
// [keep-thunk-extern] compile-flags: -Zfunction-return=keep -Zfunction-return=thunk-extern
// [thunk-extern-keep] compile-flags: -Zfunction-return=thunk-extern -Zfunction-return=keep

#![crate_type = "lib"]
#![feature(no_core, lang_items)]
#![no_core]

#[lang = "sized"]
trait Sized {}

#[no_mangle]
pub fn foo() {
// CHECK: @foo() unnamed_addr #0

// unset-NOT: fn_ret_thunk_extern
// keep-NOT: fn_ret_thunk_extern
// thunk-extern: attributes #0 = { {{.*}}fn_ret_thunk_extern{{.*}} }
// keep-thunk-extern: attributes #0 = { {{.*}}fn_ret_thunk_extern{{.*}} }
// thunk-extern-keep-NOT: fn_ret_thunk_extern
}

0 comments on commit 6a0ae7d

Please sign in to comment.