Skip to content

Commit fd5454e

Browse files
authored
Unrolled build for #124741
Rollup merge of #124741 - nebulark:patchable-function-entries-pr, r=estebank,workingjubilee patchable-function-entry: Add unstable compiler flag and attribute Tracking issue: #123115 Add the -Z patchable-function-entry compiler flag and the #[patchable_function_entry(prefix_nops = m, entry_nops = n)] attribute. Rebased and adjusted the canditate implementation to match changes in the RFC.
2 parents 42add88 + 8d246b0 commit fd5454e

19 files changed

+478
-7
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use rustc_codegen_ssa::traits::*;
44
use rustc_hir::def_id::DefId;
5-
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
5+
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry};
66
use rustc_middle::ty::{self, TyCtxt};
77
use rustc_session::config::{FunctionReturn, OptLevel};
88
use rustc_span::symbol::sym;
@@ -53,6 +53,34 @@ fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll
5353
}
5454
}
5555

56+
#[inline]
57+
fn patchable_function_entry_attrs<'ll>(
58+
cx: &CodegenCx<'ll, '_>,
59+
attr: Option<PatchableFunctionEntry>,
60+
) -> SmallVec<[&'ll Attribute; 2]> {
61+
let mut attrs = SmallVec::new();
62+
let patchable_spec = attr.unwrap_or_else(|| {
63+
PatchableFunctionEntry::from_config(cx.tcx.sess.opts.unstable_opts.patchable_function_entry)
64+
});
65+
let entry = patchable_spec.entry();
66+
let prefix = patchable_spec.prefix();
67+
if entry > 0 {
68+
attrs.push(llvm::CreateAttrStringValue(
69+
cx.llcx,
70+
"patchable-function-entry",
71+
&format!("{}", entry),
72+
));
73+
}
74+
if prefix > 0 {
75+
attrs.push(llvm::CreateAttrStringValue(
76+
cx.llcx,
77+
"patchable-function-prefix",
78+
&format!("{}", prefix),
79+
));
80+
}
81+
attrs
82+
}
83+
5684
/// Get LLVM sanitize attributes.
5785
#[inline]
5886
pub fn sanitize_attrs<'ll>(
@@ -421,6 +449,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
421449
llvm::set_alignment(llfn, align);
422450
}
423451
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
452+
to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry));
424453

425454
// Always annotate functions with the target-cpu they are compiled for.
426455
// Without this, ThinLTO won't inline Rust functions into Clang generated

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+78-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
22
use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
3-
use rustc_errors::{codes::*, struct_span_code_err};
3+
use rustc_errors::{codes::*, struct_span_code_err, DiagMessage, SubdiagMessage};
44
use rustc_hir as hir;
55
use rustc_hir::def::DefKind;
66
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
77
use rustc_hir::{lang_items, weak_lang_items::WEAK_LANG_ITEMS, LangItem};
8-
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
8+
use rustc_middle::middle::codegen_fn_attrs::{
9+
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
10+
};
911
use rustc_middle::mir::mono::Linkage;
1012
use rustc_middle::query::Providers;
1113
use rustc_middle::ty::{self as ty, TyCtxt};
@@ -447,6 +449,80 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
447449
None
448450
};
449451
}
452+
sym::patchable_function_entry => {
453+
codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
454+
let mut prefix = None;
455+
let mut entry = None;
456+
for item in l {
457+
let Some(meta_item) = item.meta_item() else {
458+
tcx.dcx().span_err(item.span(), "expected name value pair");
459+
continue;
460+
};
461+
462+
let Some(name_value_lit) = meta_item.name_value_literal() else {
463+
tcx.dcx().span_err(item.span(), "expected name value pair");
464+
continue;
465+
};
466+
467+
fn emit_error_with_label(
468+
tcx: TyCtxt<'_>,
469+
span: Span,
470+
error: impl Into<DiagMessage>,
471+
label: impl Into<SubdiagMessage>,
472+
) {
473+
let mut err: rustc_errors::Diag<'_, _> =
474+
tcx.dcx().struct_span_err(span, error);
475+
err.span_label(span, label);
476+
err.emit();
477+
}
478+
479+
let attrib_to_write = match meta_item.name_or_empty() {
480+
sym::prefix_nops => &mut prefix,
481+
sym::entry_nops => &mut entry,
482+
_ => {
483+
emit_error_with_label(
484+
tcx,
485+
item.span(),
486+
"unexpected parameter name",
487+
format!("expected {} or {}", sym::prefix_nops, sym::entry_nops),
488+
);
489+
continue;
490+
}
491+
};
492+
493+
let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
494+
emit_error_with_label(
495+
tcx,
496+
name_value_lit.span,
497+
"invalid literal value",
498+
"value must be an integer between `0` and `255`",
499+
);
500+
continue;
501+
};
502+
503+
let Ok(val) = val.get().try_into() else {
504+
emit_error_with_label(
505+
tcx,
506+
name_value_lit.span,
507+
"integer value out of range",
508+
"value must be between `0` and `255`",
509+
);
510+
continue;
511+
};
512+
513+
*attrib_to_write = Some(val);
514+
}
515+
516+
if let (None, None) = (prefix, entry) {
517+
tcx.dcx().span_err(attr.span, "must specify at least one parameter");
518+
}
519+
520+
Some(PatchableFunctionEntry::from_prefix_and_entry(
521+
prefix.unwrap_or(0),
522+
entry.unwrap_or(0),
523+
))
524+
})
525+
}
450526
_ => {}
451527
}
452528
}

compiler/rustc_feature/src/builtin_attrs.rs

+7
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
585585
EncodeCrossCrate::No, derive_smart_pointer, experimental!(pointee)
586586
),
587587

588+
// RFC 3543
589+
// `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]`
590+
gated!(
591+
patchable_function_entry, Normal, template!(List: "prefix_nops = m, entry_nops = n"), ErrorPreceding,
592+
EncodeCrossCrate::Yes, experimental!(patchable_function_entry)
593+
),
594+
588595
// ==========================================================================
589596
// Internal attributes: Stability, deprecation, and unsafe:
590597
// ==========================================================================

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,8 @@ declare_features! (
563563
(unstable, offset_of_slice, "CURRENT_RUSTC_VERSION", Some(126151)),
564564
/// Allows using `#[optimize(X)]`.
565565
(unstable, optimize_attribute, "1.34.0", Some(54882)),
566+
/// Allows specifying nop padding on functions for dynamic patching.
567+
(unstable, patchable_function_entry, "CURRENT_RUSTC_VERSION", Some(123115)),
566568
/// Allows postfix match `expr.match { ... }`
567569
(unstable, postfix_match, "1.79.0", Some(121618)),
568570
/// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args.

compiler/rustc_interface/src/tests.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use rustc_session::config::{
88
ErrorOutputType, ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold,
99
Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail,
1010
LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey,
11-
PacRet, Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath,
12-
SymbolManglingVersion, WasiExecModel,
11+
PacRet, Passes, PatchableFunctionEntry, Polonius, ProcMacroExecutionStrategy, Strip,
12+
SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
1313
};
1414
use rustc_session::lint::Level;
1515
use rustc_session::search_paths::SearchPath;
@@ -813,6 +813,11 @@ fn test_unstable_options_tracking_hash() {
813813
tracked!(packed_bundled_libs, true);
814814
tracked!(panic_abort_tests, true);
815815
tracked!(panic_in_drop, PanicStrategy::Abort);
816+
tracked!(
817+
patchable_function_entry,
818+
PatchableFunctionEntry::from_total_and_prefix_nops(10, 5)
819+
.expect("total must be greater than or equal to prefix")
820+
);
816821
tracked!(plt, Some(true));
817822
tracked!(polonius, Polonius::Legacy);
818823
tracked!(precise_enum_drop_elaboration, false);

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

+27
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,32 @@ pub struct CodegenFnAttrs {
4545
/// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be
4646
/// aligned to.
4747
pub alignment: Option<Align>,
48+
/// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around
49+
/// the function entry.
50+
pub patchable_function_entry: Option<PatchableFunctionEntry>,
51+
}
52+
53+
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
54+
pub struct PatchableFunctionEntry {
55+
/// Nops to prepend to the function
56+
prefix: u8,
57+
/// Nops after entry, but before body
58+
entry: u8,
59+
}
60+
61+
impl PatchableFunctionEntry {
62+
pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self {
63+
Self { prefix: config.prefix(), entry: config.entry() }
64+
}
65+
pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self {
66+
Self { prefix, entry }
67+
}
68+
pub fn prefix(&self) -> u8 {
69+
self.prefix
70+
}
71+
pub fn entry(&self) -> u8 {
72+
self.entry
73+
}
4874
}
4975

5076
#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
@@ -121,6 +147,7 @@ impl CodegenFnAttrs {
121147
no_sanitize: SanitizerSet::empty(),
122148
instruction_set: None,
123149
alignment: None,
150+
patchable_function_entry: None,
124151
}
125152
}
126153

compiler/rustc_session/src/config.rs

+33-2
Original file line numberDiff line numberDiff line change
@@ -2965,8 +2965,9 @@ pub(crate) mod dep_tracking {
29652965
CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn,
29662966
InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
29672967
LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
2968-
Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
2969-
SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
2968+
PatchableFunctionEntry, Polonius, RemapPathScopeComponents, ResolveDocLinks,
2969+
SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
2970+
WasiExecModel,
29702971
};
29712972
use crate::lint;
29722973
use crate::utils::NativeLib;
@@ -3073,6 +3074,7 @@ pub(crate) mod dep_tracking {
30733074
OomStrategy,
30743075
LanguageIdentifier,
30753076
NextSolverConfig,
3077+
PatchableFunctionEntry,
30763078
Polonius,
30773079
InliningThreshold,
30783080
FunctionReturn,
@@ -3250,6 +3252,35 @@ impl DumpMonoStatsFormat {
32503252
}
32513253
}
32523254

3255+
/// `-Z patchable-function-entry` representation - how many nops to put before and after function
3256+
/// entry.
3257+
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3258+
pub struct PatchableFunctionEntry {
3259+
/// Nops before the entry
3260+
prefix: u8,
3261+
/// Nops after the entry
3262+
entry: u8,
3263+
}
3264+
3265+
impl PatchableFunctionEntry {
3266+
pub fn from_total_and_prefix_nops(
3267+
total_nops: u8,
3268+
prefix_nops: u8,
3269+
) -> Option<PatchableFunctionEntry> {
3270+
if total_nops < prefix_nops {
3271+
None
3272+
} else {
3273+
Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops })
3274+
}
3275+
}
3276+
pub fn prefix(&self) -> u8 {
3277+
self.prefix
3278+
}
3279+
pub fn entry(&self) -> u8 {
3280+
self.entry
3281+
}
3282+
}
3283+
32533284
/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
32543285
/// or future prototype.
32553286
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]

compiler/rustc_session/src/options.rs

+29
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ mod desc {
379379
pub const parse_passes: &str = "a space-separated list of passes, or `all`";
380380
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
381381
pub const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`";
382+
pub const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)";
382383
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
383384
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
384385
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
@@ -734,6 +735,32 @@ mod parse {
734735
true
735736
}
736737

738+
pub(crate) fn parse_patchable_function_entry(
739+
slot: &mut PatchableFunctionEntry,
740+
v: Option<&str>,
741+
) -> bool {
742+
let mut total_nops = 0;
743+
let mut prefix_nops = 0;
744+
745+
if !parse_number(&mut total_nops, v) {
746+
let parts = v.and_then(|v| v.split_once(',')).unzip();
747+
if !parse_number(&mut total_nops, parts.0) {
748+
return false;
749+
}
750+
if !parse_number(&mut prefix_nops, parts.1) {
751+
return false;
752+
}
753+
}
754+
755+
if let Some(pfe) =
756+
PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops)
757+
{
758+
*slot = pfe;
759+
return true;
760+
}
761+
false
762+
}
763+
737764
pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool {
738765
match v {
739766
Some("panic") => *slot = OomStrategy::Panic,
@@ -1859,6 +1886,8 @@ options! {
18591886
"panic strategy for panics in drops"),
18601887
parse_only: bool = (false, parse_bool, [UNTRACKED],
18611888
"parse only; do not compile, assemble, or link (default: no)"),
1889+
patchable_function_entry: PatchableFunctionEntry = (PatchableFunctionEntry::default(), parse_patchable_function_entry, [TRACKED],
1890+
"nop padding at function entry"),
18621891
plt: Option<bool> = (None, parse_opt_bool, [TRACKED],
18631892
"whether to use the PLT when calling into shared libraries;
18641893
only has effect for PIC code on systems with ELF binaries

compiler/rustc_span/src/symbol.rs

+3
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,7 @@ symbols! {
768768
enable,
769769
encode,
770770
end,
771+
entry_nops,
771772
enumerate_method,
772773
env,
773774
env_CFG_RELEASE: env!("CFG_RELEASE"),
@@ -1383,6 +1384,7 @@ symbols! {
13831384
passes,
13841385
pat,
13851386
pat_param,
1387+
patchable_function_entry,
13861388
path,
13871389
pattern_complexity,
13881390
pattern_parentheses,
@@ -1421,6 +1423,7 @@ symbols! {
14211423
prefetch_read_instruction,
14221424
prefetch_write_data,
14231425
prefetch_write_instruction,
1426+
prefix_nops,
14241427
preg,
14251428
prelude,
14261429
prelude_import,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# `patchable-function-entry`
2+
3+
--------------------
4+
5+
The `-Z patchable-function-entry=total_nops,prefix_nops` or `-Z patchable-function-entry=total_nops`
6+
compiler flag enables nop padding of function entries with 'total_nops' nops, with
7+
an offset for the entry of the function at 'prefix_nops' nops. In the second form,
8+
'prefix_nops' defaults to 0.
9+
10+
As an illustrative example, `-Z patchable-function-entry=3,2` would produce:
11+
12+
```text
13+
nop
14+
nop
15+
function_label:
16+
nop
17+
//Actual function code begins here
18+
```
19+
20+
This flag is used for hotpatching, especially in the Linux kernel. The flag
21+
arguments are modeled after the `-fpatchable-function-entry` flag as defined
22+
for both [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fpatchable-function-entry)
23+
and [gcc](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fpatchable-function-entry)
24+
and is intended to provide the same effect.

0 commit comments

Comments
 (0)