Skip to content

Commit 8f7613b

Browse files
committed
Allow section override when using patchable-function-entries
Sometimes it is necessary to group patchable function entrypoint records in distinct linker sections. This is the case for some bpf functions within the linux kernel which shouldn't be visible to ftrace. Extend `-Zpatchable-function-entry` to accept an argument of the form `prefix_nops,total_nops,record_section`, which places all entry record into a user specified section. Likewise, extend the `patchable_function_entry` attribute to accept an optional `section="name"` option to place a function into a specific section. This is made possible by llvm attribute `patchable-function-entry-section` added in llvm 21.
1 parent b3f7e32 commit 8f7613b

17 files changed

Lines changed: 201 additions & 53 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -729,14 +729,16 @@ pub(crate) struct PatchableFunctionEntryParser;
729729
impl SingleAttributeParser for PatchableFunctionEntryParser {
730730
const PATH: &[Symbol] = &[sym::patchable_function_entry];
731731
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
732-
const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);
732+
const TEMPLATE: AttributeTemplate =
733+
template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]);
733734
const STABILITY: AttributeStability = unstable!(patchable_function_entry);
734735

735736
fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
736737
let meta_item_list = cx.expect_list(args, cx.attr_span)?;
737738

738739
let mut prefix = None;
739740
let mut entry = None;
741+
let mut section = None;
740742

741743
if meta_item_list.len() == 0 {
742744
cx.adcx().expected_at_least_one_argument(meta_item_list.span);
@@ -769,6 +771,22 @@ impl SingleAttributeParser for PatchableFunctionEntryParser {
769771
}
770772
&mut entry
771773
}
774+
sym::section => {
775+
// Duplicate entries are not allowed
776+
if section.is_some() {
777+
errored = true;
778+
cx.adcx().duplicate_key(ident.span, sym::section);
779+
continue;
780+
}
781+
// Only a string type value is allowed.
782+
let Some(value_str) = value.value_as_str() else {
783+
cx.adcx().expect_string_literal(value);
784+
continue;
785+
};
786+
section = Some(value_str);
787+
// Integer parsing is not needed, process next item.
788+
continue;
789+
}
772790
_ => {
773791
errored = true;
774792
cx.adcx().expected_specific_argument(
@@ -801,10 +819,7 @@ impl SingleAttributeParser for PatchableFunctionEntryParser {
801819
if errored {
802820
None
803821
} else {
804-
Some(AttributeKind::PatchableFunctionEntry {
805-
prefix: prefix.unwrap_or(0),
806-
entry: entry.unwrap_or(0),
807-
})
822+
Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section })
808823
}
809824
}
810825
}

compiler/rustc_codegen_llvm/src/attributes.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,25 @@ fn patchable_function_entry_attrs<'ll>(
8383
attr: Option<PatchableFunctionEntry>,
8484
) -> SmallVec<[&'ll Attribute; 2]> {
8585
let mut attrs = SmallVec::new();
86-
let patchable_spec = attr.unwrap_or_else(|| {
87-
PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry)
88-
});
89-
let entry = patchable_spec.entry();
90-
let prefix = patchable_spec.prefix();
86+
87+
let mut entry = sess.opts.unstable_opts.patchable_function_entry.entry();
88+
let mut prefix = sess.opts.unstable_opts.patchable_function_entry.prefix();
89+
let mut section = sess.opts.unstable_opts.patchable_function_entry.section();
90+
let section_sym;
91+
92+
// Apply attribute specified overrides, if any.
93+
if let Some(patchable_spec) = attr {
94+
if let Some(sym) = patchable_spec.section() {
95+
section_sym = sym;
96+
section = Some(section_sym.as_str());
97+
}
98+
// Override the cmdline specified options if any nop count is specified.
99+
if patchable_spec.entry().is_some() || patchable_spec.prefix().is_some() {
100+
entry = patchable_spec.entry().unwrap_or(0);
101+
prefix = patchable_spec.prefix().unwrap_or(0);
102+
}
103+
}
104+
91105
if entry > 0 {
92106
attrs.push(llvm::CreateAttrStringValue(
93107
cx.llcx,
@@ -102,6 +116,13 @@ fn patchable_function_entry_attrs<'ll>(
102116
&format!("{}", prefix),
103117
));
104118
}
119+
if let Some(section) = section {
120+
attrs.push(llvm::CreateAttrStringValue(
121+
cx.llcx,
122+
"patchable-function-entry-section",
123+
section,
124+
));
125+
}
105126
attrs
106127
}
107128

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN};
1414
use rustc_data_structures::fx::FxHashMap;
1515
use rustc_data_structures::small_c_str::SmallCStr;
1616
use rustc_hir::def_id::DefId;
17-
use rustc_middle::middle::codegen_fn_attrs::PatchableFunctionEntry;
1817
use rustc_middle::mono::CodegenUnit;
1918
use rustc_middle::ty::layout::{
2019
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers,
@@ -335,14 +334,13 @@ pub(crate) unsafe fn create_module<'ll>(
335334

336335
// Add "kcfi-offset" module flag with -Z patchable-function-entry (See
337336
// https://reviews.llvm.org/D141172).
338-
let pfe =
339-
PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry);
340-
if pfe.prefix() > 0 {
337+
let patchable_prefix_nops = sess.opts.unstable_opts.patchable_function_entry.prefix();
338+
if patchable_prefix_nops > 0 {
341339
llvm::add_module_flag_u32(
342340
llmod,
343341
llvm::ModuleFlagMergeBehavior::Override,
344342
"kcfi-offset",
345-
pfe.prefix().into(),
343+
patchable_prefix_nops.into(),
346344
);
347345
}
348346

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,11 @@ fn process_builtin_attrs(
289289
AttributeKind::RustcOffloadKernel => {
290290
codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL
291291
}
292-
AttributeKind::PatchableFunctionEntry { prefix, entry } => {
292+
AttributeKind::PatchableFunctionEntry { prefix, entry, section } => {
293293
codegen_fn_attrs.patchable_function_entry =
294-
Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry));
294+
Some(PatchableFunctionEntry::from_prefix_entry_and_section(
295+
*prefix, *entry, *section,
296+
));
295297
}
296298
_ => {}
297299
}

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,8 +1199,9 @@ pub enum AttributeKind {
11991199

12001200
/// Represents `#[patchable_function_entry]`
12011201
PatchableFunctionEntry {
1202-
prefix: u8,
1203-
entry: u8,
1202+
prefix: Option<u8>,
1203+
entry: Option<u8>,
1204+
section: Option<Symbol>,
12041205
},
12051206

12061207
/// Represents `#[path]`

compiler/rustc_interface/src/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ fn test_unstable_options_tracking_hash() {
839839
tracked!(panic_in_drop, PanicStrategy::Abort);
840840
tracked!(
841841
patchable_function_entry,
842-
PatchableFunctionEntry::from_total_and_prefix_nops(10, 5)
842+
PatchableFunctionEntry::from_parts(10, 5, None)
843843
.expect("total must be greater than or equal to prefix")
844844
);
845845
tracked!(plt, Some(true));

compiler/rustc_middle/src/middle/codegen_fn_attrs.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ pub struct CodegenFnAttrs {
103103
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
104104
pub alignment: Option<Align>,
105105
/// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around
106-
/// the function entry.
106+
/// the function entry, or override default section to record entry location.
107107
pub patchable_function_entry: Option<PatchableFunctionEntry>,
108108
/// The `#[rustc_objc_class = "..."]` attribute.
109109
pub objc_class: Option<Symbol>,
@@ -133,24 +133,33 @@ pub struct TargetFeature {
133133
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)]
134134
pub struct PatchableFunctionEntry {
135135
/// Nops to prepend to the function
136-
prefix: u8,
136+
prefix: Option<u8>,
137137
/// Nops after entry, but before body
138-
entry: u8,
138+
entry: Option<u8>,
139+
/// Optional, specific section to record entry location in
140+
section: Option<Symbol>,
139141
}
140142

141143
impl PatchableFunctionEntry {
142-
pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self {
143-
Self { prefix: config.prefix(), entry: config.entry() }
144+
pub fn from_prefix_entry_and_section(
145+
prefix: Option<u8>,
146+
entry: Option<u8>,
147+
section: Option<Symbol>,
148+
) -> Self {
149+
Self { prefix, entry, section }
144150
}
145151
pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self {
146-
Self { prefix, entry }
152+
Self { prefix: Some(prefix), entry: Some(entry), section: None }
147153
}
148-
pub fn prefix(&self) -> u8 {
154+
pub fn prefix(&self) -> Option<u8> {
149155
self.prefix
150156
}
151-
pub fn entry(&self) -> u8 {
157+
pub fn entry(&self) -> Option<u8> {
152158
self.entry
153159
}
160+
pub fn section(&self) -> Option<Symbol> {
161+
self.section
162+
}
154163
}
155164

156165
#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, StableHash)]

compiler/rustc_session/src/config.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3300,23 +3300,26 @@ impl DumpMonoStatsFormat {
33003300

33013301
/// `-Z patchable-function-entry` representation - how many nops to put before and after function
33023302
/// entry.
3303-
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3303+
#[derive(Clone, PartialEq, Hash, Debug, Default)]
33043304
pub struct PatchableFunctionEntry {
33053305
/// Nops before the entry
33063306
prefix: u8,
33073307
/// Nops after the entry
33083308
entry: u8,
3309+
/// An optional section name to record the entry location
3310+
section: Option<String>,
33093311
}
33103312

33113313
impl PatchableFunctionEntry {
3312-
pub fn from_total_and_prefix_nops(
3314+
pub fn from_parts(
33133315
total_nops: u8,
33143316
prefix_nops: u8,
3317+
section: Option<String>,
33153318
) -> Option<PatchableFunctionEntry> {
33163319
if total_nops < prefix_nops {
33173320
None
33183321
} else {
3319-
Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops })
3322+
Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops, section })
33203323
}
33213324
}
33223325
pub fn prefix(&self) -> u8 {
@@ -3325,6 +3328,9 @@ impl PatchableFunctionEntry {
33253328
pub fn entry(&self) -> u8 {
33263329
self.entry
33273330
}
3331+
pub fn section(&self) -> Option<&str> {
3332+
self.section.as_ref().map(|x| x.as_str())
3333+
}
33283334
}
33293335

33303336
/// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,

compiler/rustc_session/src/options.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ mod desc {
765765
pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`";
766766
pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`";
767767
pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`";
768-
pub(crate) 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)";
768+
pub(crate) const parse_patchable_function_entry: &str = "a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops";
769769
pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy;
770770
pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
771771
pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `kernel-hwaddress`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`, or 'realtime'";
@@ -1184,20 +1184,24 @@ pub mod parse {
11841184
) -> bool {
11851185
let mut total_nops = 0;
11861186
let mut prefix_nops = 0;
1187+
let mut section = None;
11871188

11881189
if !parse_number(&mut total_nops, v) {
1189-
let parts = v.and_then(|v| v.split_once(',')).unzip();
1190-
if !parse_number(&mut total_nops, parts.0) {
1190+
let parts: Vec<_> = v.unwrap_or("").split(',').collect();
1191+
if parts.len() < 2 || parts.len() > 3 {
11911192
return false;
11921193
}
1193-
if !parse_number(&mut prefix_nops, parts.1) {
1194+
1195+
if !parse_number(&mut total_nops, Some(parts[0])) {
1196+
return false;
1197+
}
1198+
if !parse_number(&mut prefix_nops, Some(parts[1])) {
11941199
return false;
11951200
}
1201+
section = parts.get(2).map(|x| x.to_string());
11961202
}
11971203

1198-
if let Some(pfe) =
1199-
PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops)
1200-
{
1204+
if let Some(pfe) = PatchableFunctionEntry::from_parts(total_nops, prefix_nops, section) {
12011205
*slot = pfe;
12021206
return true;
12031207
}

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,7 @@ symbols! {
18321832
saturating_sub,
18331833
sdylib,
18341834
search_unbox,
1835+
section,
18351836
select_unpredictable,
18361837
self_in_typedefs,
18371838
self_struct_ctor,

0 commit comments

Comments
 (0)