mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
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
This commit is contained in:
parent
d929a42a66
commit
ac7595fdb1
@ -53,6 +53,31 @@ fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn patchable_function_entry_attrs<'ll>(
|
||||||
|
cx: &CodegenCx<'ll, '_>,
|
||||||
|
) -> SmallVec<[&'ll Attribute; 2]> {
|
||||||
|
let mut attrs = SmallVec::new();
|
||||||
|
let patchable_spec = cx.tcx.sess.opts.unstable_opts.patchable_function_entry;
|
||||||
|
let entry = patchable_spec.entry();
|
||||||
|
let prefix = patchable_spec.prefix();
|
||||||
|
if entry > 0 {
|
||||||
|
attrs.push(llvm::CreateAttrStringValue(
|
||||||
|
cx.llcx,
|
||||||
|
"patchable-function-entry",
|
||||||
|
&format!("{}", entry),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if prefix > 0 {
|
||||||
|
attrs.push(llvm::CreateAttrStringValue(
|
||||||
|
cx.llcx,
|
||||||
|
"patchable-function-prefix",
|
||||||
|
&format!("{}", prefix),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
attrs
|
||||||
|
}
|
||||||
|
|
||||||
/// Get LLVM sanitize attributes.
|
/// Get LLVM sanitize attributes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn sanitize_attrs<'ll>(
|
pub fn sanitize_attrs<'ll>(
|
||||||
@ -421,6 +446,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
|
|||||||
llvm::set_alignment(llfn, align);
|
llvm::set_alignment(llfn, align);
|
||||||
}
|
}
|
||||||
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
|
to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
|
||||||
|
to_add.extend(patchable_function_entry_attrs(cx));
|
||||||
|
|
||||||
// Always annotate functions with the target-cpu they are compiled for.
|
// Always annotate functions with the target-cpu they are compiled for.
|
||||||
// Without this, ThinLTO won't inline Rust functions into Clang generated
|
// Without this, ThinLTO won't inline Rust functions into Clang generated
|
||||||
|
@ -813,6 +813,7 @@ fn test_unstable_options_tracking_hash() {
|
|||||||
tracked!(packed_bundled_libs, true);
|
tracked!(packed_bundled_libs, true);
|
||||||
tracked!(panic_abort_tests, true);
|
tracked!(panic_abort_tests, true);
|
||||||
tracked!(panic_in_drop, PanicStrategy::Abort);
|
tracked!(panic_in_drop, PanicStrategy::Abort);
|
||||||
|
tracked!(patchable_function_entry, PatchableFunctionEntry::from_nop_count_and_offset(3, 4));
|
||||||
tracked!(plt, Some(true));
|
tracked!(plt, Some(true));
|
||||||
tracked!(polonius, Polonius::Legacy);
|
tracked!(polonius, Polonius::Legacy);
|
||||||
tracked!(precise_enum_drop_elaboration, false);
|
tracked!(precise_enum_drop_elaboration, false);
|
||||||
|
@ -47,6 +47,29 @@ pub struct CodegenFnAttrs {
|
|||||||
pub alignment: Option<Align>,
|
pub alignment: Option<Align>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||||
|
pub struct PatchableFunctionEntry {
|
||||||
|
/// Nops to prepend to the function
|
||||||
|
prefix: u8,
|
||||||
|
/// Nops after entry, but before body
|
||||||
|
entry: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PatchableFunctionEntry {
|
||||||
|
pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self {
|
||||||
|
Self { prefix: config.prefix(), entry: config.entry() }
|
||||||
|
}
|
||||||
|
pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self {
|
||||||
|
Self { prefix, entry }
|
||||||
|
}
|
||||||
|
pub fn prefix(&self) -> u8 {
|
||||||
|
self.prefix
|
||||||
|
}
|
||||||
|
pub fn entry(&self) -> u8 {
|
||||||
|
self.entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
|
#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
|
||||||
pub struct CodegenFnAttrFlags(u32);
|
pub struct CodegenFnAttrFlags(u32);
|
||||||
bitflags::bitflags! {
|
bitflags::bitflags! {
|
||||||
|
@ -2963,7 +2963,7 @@ pub(crate) mod dep_tracking {
|
|||||||
CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn,
|
CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn,
|
||||||
InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
|
InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
|
||||||
LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
|
LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
|
||||||
Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
|
PatchableFunctionEntry, Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
|
||||||
SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
|
SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
|
||||||
};
|
};
|
||||||
use crate::lint;
|
use crate::lint;
|
||||||
@ -3071,6 +3071,7 @@ pub(crate) mod dep_tracking {
|
|||||||
OomStrategy,
|
OomStrategy,
|
||||||
LanguageIdentifier,
|
LanguageIdentifier,
|
||||||
NextSolverConfig,
|
NextSolverConfig,
|
||||||
|
PatchableFunctionEntry,
|
||||||
Polonius,
|
Polonius,
|
||||||
InliningThreshold,
|
InliningThreshold,
|
||||||
FunctionReturn,
|
FunctionReturn,
|
||||||
@ -3248,6 +3249,32 @@ impl DumpMonoStatsFormat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `-Z patchable-function-entry` representation - how many nops to put before and after function
|
||||||
|
/// entry.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
|
||||||
|
pub struct PatchableFunctionEntry {
|
||||||
|
/// Nops before the entry
|
||||||
|
prefix: u8,
|
||||||
|
/// Nops after the entry
|
||||||
|
entry: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PatchableFunctionEntry {
|
||||||
|
pub fn from_nop_count_and_offset(nop_count: u8, offset: u8) -> Option<PatchableFunctionEntry> {
|
||||||
|
if nop_count < offset {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Self { prefix: offset, entry: nop_count - offset })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn prefix(&self) -> u8 {
|
||||||
|
self.prefix
|
||||||
|
}
|
||||||
|
pub fn entry(&self) -> u8 {
|
||||||
|
self.entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
|
/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
|
||||||
/// or future prototype.
|
/// or future prototype.
|
||||||
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
|
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
|
||||||
|
@ -379,6 +379,8 @@ mod desc {
|
|||||||
pub const parse_passes: &str = "a space-separated list of passes, or `all`";
|
pub const parse_passes: &str = "a space-separated list of passes, or `all`";
|
||||||
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
|
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
|
||||||
pub const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`";
|
pub const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`";
|
||||||
|
pub const parse_patchable_function_entry: &str =
|
||||||
|
"nop_count,entry_offset or nop_count (defaulting entry_offset=0)";
|
||||||
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
|
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
|
||||||
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
|
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
|
||||||
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
|
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
|
||||||
@ -723,6 +725,7 @@ mod parse {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn parse_on_broken_pipe(slot: &mut OnBrokenPipe, v: Option<&str>) -> bool {
|
pub(crate) fn parse_on_broken_pipe(slot: &mut OnBrokenPipe, v: Option<&str>) -> bool {
|
||||||
match v {
|
match v {
|
||||||
// OnBrokenPipe::Default can't be explicitly specified
|
// OnBrokenPipe::Default can't be explicitly specified
|
||||||
@ -734,6 +737,30 @@ mod parse {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_patchable_function_entry(
|
||||||
|
slot: &mut PatchableFunctionEntry,
|
||||||
|
v: Option<&str>,
|
||||||
|
) -> bool {
|
||||||
|
let mut nop_count = 0;
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
if !parse_number(&mut nop_count, v) {
|
||||||
|
let parts = v.and_then(|v| v.split_once(',')).unzip();
|
||||||
|
if !parse_number(&mut nop_count, parts.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !parse_number(&mut offset, parts.1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pfe) = PatchableFunctionEntry::from_nop_count_and_offset(nop_count, offset) {
|
||||||
|
*slot = pfe;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool {
|
pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool {
|
||||||
match v {
|
match v {
|
||||||
Some("panic") => *slot = OomStrategy::Panic,
|
Some("panic") => *slot = OomStrategy::Panic,
|
||||||
@ -1859,6 +1886,8 @@ options! {
|
|||||||
"panic strategy for panics in drops"),
|
"panic strategy for panics in drops"),
|
||||||
parse_only: bool = (false, parse_bool, [UNTRACKED],
|
parse_only: bool = (false, parse_bool, [UNTRACKED],
|
||||||
"parse only; do not compile, assemble, or link (default: no)"),
|
"parse only; do not compile, assemble, or link (default: no)"),
|
||||||
|
patchable_function_entry: PatchableFunctionEntry = (PatchableFunctionEntry::default(), parse_patchable_function_entry, [TRACKED],
|
||||||
|
"nop padding at function entry"),
|
||||||
plt: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
plt: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||||
"whether to use the PLT when calling into shared libraries;
|
"whether to use the PLT when calling into shared libraries;
|
||||||
only has effect for PIC code on systems with ELF binaries
|
only has effect for PIC code on systems with ELF binaries
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
# `patchable-function-entry`
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
The `-Z patchable-function-entry=M,N` or `-Z patchable-function-entry=M`
|
||||||
|
compiler flag enables nop padding of function entries with M nops, with
|
||||||
|
an offset for the entry of the function at N nops. In the second form,
|
||||||
|
N defaults to 0.
|
||||||
|
|
||||||
|
As an illustrative example, `-Z patchable-function-entry=3,2` would produce:
|
||||||
|
|
||||||
|
```
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
function_label:
|
||||||
|
nop
|
||||||
|
//Actual function code begins here
|
||||||
|
```
|
||||||
|
|
||||||
|
This flag is used for hotpatching, especially in the Linux kernel. The flag
|
||||||
|
arguments are modeled after hte `-fpatchable-function-entry` flag as defined
|
||||||
|
for both [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fpatchable-function-entry)
|
||||||
|
and [gcc](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fpatchable-function-entry)
|
||||||
|
and is intended to provide the same effect.
|
8
tests/codegen/patchable-function-entry.rs
Normal file
8
tests/codegen/patchable-function-entry.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// compile-flags: -Z patchable-function-entry=15,10
|
||||||
|
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn foo() {}
|
||||||
|
// CHECK: @foo() unnamed_addr #0
|
||||||
|
// CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-prefix"="10" {{.*}} }
|
Loading…
Reference in New Issue
Block a user