about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs26
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs23
-rw-r--r--compiler/rustc_session/src/config.rs29
-rw-r--r--compiler/rustc_session/src/options.rs29
-rw-r--r--src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md24
-rw-r--r--tests/codegen/patchable-function-entry.rs8
7 files changed, 139 insertions, 1 deletions
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 48693895da1..7cf789ab5d7 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -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.
 #[inline]
 pub fn sanitize_attrs<'ll>(
@@ -421,6 +446,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
         llvm::set_alignment(llfn, align);
     }
     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.
     // Without this, ThinLTO won't inline Rust functions into Clang generated
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 6ffc518097e..e4b50e7f5ff 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -813,6 +813,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(packed_bundled_libs, true);
     tracked!(panic_abort_tests, true);
     tracked!(panic_in_drop, PanicStrategy::Abort);
+    tracked!(patchable_function_entry, PatchableFunctionEntry::from_nop_count_and_offset(3, 4));
     tracked!(plt, Some(true));
     tracked!(polonius, Polonius::Legacy);
     tracked!(precise_enum_drop_elaboration, false);
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 3fa5054baed..0fce26dcbbd 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -47,6 +47,29 @@ pub struct CodegenFnAttrs {
     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)]
 pub struct CodegenFnAttrFlags(u32);
 bitflags::bitflags! {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 839cc51efce..2534152267e 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2963,7 +2963,7 @@ pub(crate) mod dep_tracking {
         CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn,
         InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
         LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
-        Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
+        PatchableFunctionEntry, Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
         SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
     };
     use crate::lint;
@@ -3071,6 +3071,7 @@ pub(crate) mod dep_tracking {
         OomStrategy,
         LanguageIdentifier,
         NextSolverConfig,
+        PatchableFunctionEntry,
         Polonius,
         InliningThreshold,
         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,
 /// or future prototype.
 #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 9a10adeb6d1..f4f8a16662d 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -379,6 +379,8 @@ mod desc {
     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_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_oom_strategy: &str = "either `panic` or `abort`";
     pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
@@ -723,6 +725,7 @@ mod parse {
         true
     }
 
+
     pub(crate) fn parse_on_broken_pipe(slot: &mut OnBrokenPipe, v: Option<&str>) -> bool {
         match v {
             // OnBrokenPipe::Default can't be explicitly specified
@@ -734,6 +737,30 @@ mod parse {
         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 {
         match v {
             Some("panic") => *slot = OomStrategy::Panic,
@@ -1859,6 +1886,8 @@ options! {
         "panic strategy for panics in drops"),
     parse_only: bool = (false, parse_bool, [UNTRACKED],
         "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],
         "whether to use the PLT when calling into shared libraries;
         only has effect for PIC code on systems with ELF binaries
diff --git a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md
new file mode 100644
index 00000000000..a701b9e3771
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md
@@ -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.
diff --git a/tests/codegen/patchable-function-entry.rs b/tests/codegen/patchable-function-entry.rs
new file mode 100644
index 00000000000..f06739303d2
--- /dev/null
+++ b/tests/codegen/patchable-function-entry.rs
@@ -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" {{.*}} }