Skip to content

Commit 61d5019

Browse files
maurernebulark
authored andcommitted
Support for -Z patchable-function-entry
`-Z patchable-function-entry` works like `-fpatchable-function-entry` on clang/gcc. The arguments are total nop count and function offset. See MCP rust-lang/compiler-team#704
1 parent d71b3f4 commit 61d5019

File tree

7 files changed

+142
-1
lines changed

7 files changed

+142
-1
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+26
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,31 @@ 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+
) -> SmallVec<[&'ll Attribute; 2]> {
60+
let mut attrs = SmallVec::new();
61+
let patchable_spec = cx.tcx.sess.opts.unstable_opts.patchable_function_entry;
62+
let entry = patchable_spec.entry();
63+
let prefix = patchable_spec.prefix();
64+
if entry > 0 {
65+
attrs.push(llvm::CreateAttrStringValue(
66+
cx.llcx,
67+
"patchable-function-entry",
68+
&format!("{}", entry),
69+
));
70+
}
71+
if prefix > 0 {
72+
attrs.push(llvm::CreateAttrStringValue(
73+
cx.llcx,
74+
"patchable-function-prefix",
75+
&format!("{}", prefix),
76+
));
77+
}
78+
attrs
79+
}
80+
5681
/// Get LLVM sanitize attributes.
5782
#[inline]
5883
pub fn sanitize_attrs<'ll>(
@@ -420,6 +445,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
420445
llvm::set_alignment(llfn, align);
421446
}
422447
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
448+
to_add.extend(patchable_function_entry_attrs(cx));
423449

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

compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,7 @@ fn test_unstable_options_tracking_hash() {
815815
tracked!(packed_bundled_libs, true);
816816
tracked!(panic_abort_tests, true);
817817
tracked!(panic_in_drop, PanicStrategy::Abort);
818+
tracked!(patchable_function_entry, PatchableFunctionEntry::from_nop_count_and_offset(3, 4));
818819
tracked!(plt, Some(true));
819820
tracked!(polonius, Polonius::Legacy);
820821
tracked!(precise_enum_drop_elaboration, false);

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

+23
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@ pub struct CodegenFnAttrs {
4747
pub alignment: Option<Align>,
4848
}
4949

50+
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
51+
pub struct PatchableFunctionEntry {
52+
/// Nops to prepend to the function
53+
prefix: u8,
54+
/// Nops after entry, but before body
55+
entry: u8,
56+
}
57+
58+
impl PatchableFunctionEntry {
59+
pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self {
60+
Self { prefix: config.prefix(), entry: config.entry() }
61+
}
62+
pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self {
63+
Self { prefix, entry }
64+
}
65+
pub fn prefix(&self) -> u8 {
66+
self.prefix
67+
}
68+
pub fn entry(&self) -> u8 {
69+
self.entry
70+
}
71+
}
72+
5073
#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
5174
pub struct CodegenFnAttrFlags(u32);
5275
bitflags::bitflags! {

compiler/rustc_session/src/config.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -2898,7 +2898,7 @@ pub(crate) mod dep_tracking {
28982898
CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn,
28992899
InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
29002900
LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
2901-
Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
2901+
PatchableFunctionEntry, Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
29022902
SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
29032903
};
29042904
use crate::lint;
@@ -3007,6 +3007,7 @@ pub(crate) mod dep_tracking {
30073007
OomStrategy,
30083008
LanguageIdentifier,
30093009
NextSolverConfig,
3010+
PatchableFunctionEntry,
30103011
Polonius,
30113012
InliningThreshold,
30123013
FunctionReturn,
@@ -3184,6 +3185,32 @@ impl DumpMonoStatsFormat {
31843185
}
31853186
}
31863187

3188+
/// `-Z patchable-function-entry` representation - how many nops to put before and after function
3189+
/// entry.
3190+
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3191+
pub struct PatchableFunctionEntry {
3192+
/// Nops before the entry
3193+
prefix: u8,
3194+
/// Nops after the entry
3195+
entry: u8,
3196+
}
3197+
3198+
impl PatchableFunctionEntry {
3199+
pub fn from_nop_count_and_offset(nop_count: u8, offset: u8) -> Option<PatchableFunctionEntry> {
3200+
if nop_count < offset {
3201+
None
3202+
} else {
3203+
Some(Self { prefix: offset, entry: nop_count - offset })
3204+
}
3205+
}
3206+
pub fn prefix(&self) -> u8 {
3207+
self.prefix
3208+
}
3209+
pub fn entry(&self) -> u8 {
3210+
self.entry
3211+
}
3212+
}
3213+
31873214
/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
31883215
/// or future prototype.
31893216
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]

compiler/rustc_session/src/options.rs

+32
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,12 @@ mod desc {
378378
pub const parse_time_passes_format: &str = "`text` (default) or `json`";
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`";
381+
<<<<<<< HEAD
381382
pub const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`";
383+
=======
384+
pub const parse_patchable_function_entry: &str =
385+
"nop_count,entry_offset or nop_count (defaulting entry_offset=0)";
386+
>>>>>>> 7814dd86eb5 (Support for -Z patchable-function-entry)
382387
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
383388
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
384389
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
@@ -709,6 +714,7 @@ mod parse {
709714
true
710715
}
711716

717+
712718
pub(crate) fn parse_on_broken_pipe(slot: &mut OnBrokenPipe, v: Option<&str>) -> bool {
713719
match v {
714720
// OnBrokenPipe::Default can't be explicitly specified
@@ -720,6 +726,30 @@ mod parse {
720726
true
721727
}
722728

729+
pub(crate) fn parse_patchable_function_entry(
730+
slot: &mut PatchableFunctionEntry,
731+
v: Option<&str>,
732+
) -> bool {
733+
let mut nop_count = 0;
734+
let mut offset = 0;
735+
736+
if !parse_number(&mut nop_count, v) {
737+
let parts = v.and_then(|v| v.split_once(',')).unzip();
738+
if !parse_number(&mut nop_count, parts.0) {
739+
return false;
740+
}
741+
if !parse_number(&mut offset, parts.1) {
742+
return false;
743+
}
744+
}
745+
746+
if let Some(pfe) = PatchableFunctionEntry::from_nop_count_and_offset(nop_count, offset) {
747+
*slot = pfe;
748+
return true;
749+
}
750+
false
751+
}
752+
723753
pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool {
724754
match v {
725755
Some("panic") => *slot = OomStrategy::Panic,
@@ -1859,6 +1889,8 @@ options! {
18591889
"panic strategy for panics in drops"),
18601890
parse_only: bool = (false, parse_bool, [UNTRACKED],
18611891
"parse only; do not compile, assemble, or link (default: no)"),
1892+
patchable_function_entry: PatchableFunctionEntry = (PatchableFunctionEntry::default(), parse_patchable_function_entry, [TRACKED],
1893+
"nop padding at function entry"),
18621894
plt: Option<bool> = (None, parse_opt_bool, [TRACKED],
18631895
"whether to use the PLT when calling into shared libraries;
18641896
only has effect for PIC code on systems with ELF binaries
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=M,N` or `-Z patchable-function-entry=M`
6+
compiler flag enables nop padding of function entries with M nops, with
7+
an offset for the entry of the function at N nops. In the second form,
8+
N defaults to 0.
9+
10+
As an illustrative example, `-Z patchable-function-entry=3,2` would produce:
11+
12+
```
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 hte `-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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: -Z patchable-function-entry=15,10
2+
3+
#![crate_type = "lib"]
4+
5+
#[no_mangle]
6+
pub fn foo() {}
7+
// CHECK: @foo() unnamed_addr #0
8+
// CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-prefix"="10" {{.*}} }

0 commit comments

Comments
 (0)