Skip to content

Commit c729ed3

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 973ad0d commit c729ed3

18 files changed

Lines changed: 229 additions & 56 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use super::prelude::*;
99
use crate::attributes::AttributeSafety;
1010
use crate::session_diagnostics::{
1111
EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,
12-
NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
13-
SanitizeInvalidStatic,
12+
NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral,
13+
ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic,
1414
};
1515
use crate::target_checking::Policy::AllowSilent;
1616

@@ -781,14 +781,16 @@ pub(crate) struct PatchableFunctionEntryParser;
781781
impl SingleAttributeParser for PatchableFunctionEntryParser {
782782
const PATH: &[Symbol] = &[sym::patchable_function_entry];
783783
const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
784-
const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);
784+
const TEMPLATE: AttributeTemplate =
785+
template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]);
785786
const STABILITY: AttributeStability = unstable!(patchable_function_entry);
786787

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

790791
let mut prefix = None;
791792
let mut entry = None;
793+
let mut section = None;
792794

793795
if meta_item_list.len() == 0 {
794796
cx.adcx().expected_at_least_one_argument(meta_item_list.span);
@@ -821,6 +823,27 @@ impl SingleAttributeParser for PatchableFunctionEntryParser {
821823
}
822824
&mut entry
823825
}
826+
sym::section => {
827+
// Duplicate entries are not allowed
828+
if section.is_some() {
829+
errored = true;
830+
cx.adcx().duplicate_key(ident.span, sym::section);
831+
continue;
832+
}
833+
// Only a string type value is allowed.
834+
let Some(value_str) = value.value_as_str() else {
835+
cx.adcx().expect_string_literal(value);
836+
continue;
837+
};
838+
// The section name does not allow null characters.
839+
if value_str.as_str().contains('\0') {
840+
errored = true;
841+
cx.emit_err(NullOnSection { span: value.value_span });
842+
}
843+
section = Some(value_str);
844+
// Integer parsing is not needed, process next item.
845+
continue;
846+
}
824847
_ => {
825848
errored = true;
826849
cx.adcx().expected_specific_argument(
@@ -853,10 +876,7 @@ impl SingleAttributeParser for PatchableFunctionEntryParser {
853876
if errored {
854877
None
855878
} else {
856-
Some(AttributeKind::PatchableFunctionEntry {
857-
prefix: prefix.unwrap_or(0),
858-
entry: entry.unwrap_or(0),
859-
})
879+
Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section })
860880
}
861881
}
862882
}

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,13 @@ pub(crate) struct NullOnObjcSelector {
300300
pub span: Span,
301301
}
302302

303+
#[derive(Diagnostic)]
304+
#[diag("`section` may not contain null characters", code = E0648)]
305+
pub(crate) struct NullOnSection {
306+
#[primary_span]
307+
pub span: Span,
308+
}
309+
303310
#[derive(Diagnostic)]
304311
#[diag("`objc::class!` expected a string literal")]
305312
pub(crate) struct ObjcClassExpectedStringLiteral {

compiler/rustc_codegen_llvm/src/attributes.rs

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

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,
@@ -343,14 +342,13 @@ pub(crate) unsafe fn create_module<'ll>(
343342

344343
// Add "kcfi-offset" module flag with -Z patchable-function-entry (See
345344
// https://reviews.llvm.org/D141172).
346-
let pfe =
347-
PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry);
348-
if pfe.prefix() > 0 {
345+
let patchable_prefix_nops = sess.opts.unstable_opts.patchable_function_entry.prefix();
346+
if patchable_prefix_nops > 0 {
349347
llvm::add_module_flag_u32(
350348
llmod,
351349
llvm::ModuleFlagMergeBehavior::Override,
352350
"kcfi-offset",
353-
pfe.prefix().into(),
351+
patchable_prefix_nops.into(),
354352
);
355353
}
356354

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,11 @@ fn process_builtin_attrs(
290290
AttributeKind::RustcOffloadKernel => {
291291
codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL
292292
}
293-
AttributeKind::PatchableFunctionEntry { prefix, entry } => {
293+
AttributeKind::PatchableFunctionEntry { prefix, entry, section } => {
294294
codegen_fn_attrs.patchable_function_entry =
295-
Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry));
295+
Some(PatchableFunctionEntry::from_prefix_entry_and_section(
296+
*prefix, *entry, *section,
297+
));
296298
}
297299
AttributeKind::InstrumentFn(instrument_fn) => {
298300
codegen_fn_attrs.instrument_fn = match instrument_fn {

compiler/rustc_hir/src/attrs/data_structures.rs

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

12251225
/// Represents `#[patchable_function_entry]`
12261226
PatchableFunctionEntry {
1227-
prefix: u8,
1228-
entry: u8,
1227+
prefix: Option<u8>,
1228+
entry: Option<u8>,
1229+
section: Option<Symbol>,
12291230
},
12301231

12311232
/// Represents `#[path]`

compiler/rustc_interface/src/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ fn test_unstable_options_tracking_hash() {
866866
tracked!(panic_in_drop, PanicStrategy::Abort);
867867
tracked!(
868868
patchable_function_entry,
869-
PatchableFunctionEntry::from_total_and_prefix_nops(10, 5)
869+
PatchableFunctionEntry::from_parts(10, 5, None)
870870
.expect("total must be greater than or equal to prefix")
871871
);
872872
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
@@ -114,7 +114,7 @@ pub struct CodegenFnAttrs {
114114
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
115115
pub alignment: Option<Align>,
116116
/// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around
117-
/// the function entry.
117+
/// the function entry, or override default section to record entry location.
118118
pub patchable_function_entry: Option<PatchableFunctionEntry>,
119119
/// The `#[rustc_objc_class = "..."]` attribute.
120120
pub objc_class: Option<Symbol>,
@@ -162,24 +162,33 @@ pub struct TargetFeature {
162162
#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)]
163163
pub struct PatchableFunctionEntry {
164164
/// Nops to prepend to the function
165-
prefix: u8,
165+
prefix: Option<u8>,
166166
/// Nops after entry, but before body
167-
entry: u8,
167+
entry: Option<u8>,
168+
/// Optional, specific section to record entry location in
169+
section: Option<Symbol>,
168170
}
169171

170172
impl PatchableFunctionEntry {
171-
pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self {
172-
Self { prefix: config.prefix(), entry: config.entry() }
173+
pub fn from_prefix_entry_and_section(
174+
prefix: Option<u8>,
175+
entry: Option<u8>,
176+
section: Option<Symbol>,
177+
) -> Self {
178+
Self { prefix, entry, section }
173179
}
174180
pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self {
175-
Self { prefix, entry }
181+
Self { prefix: Some(prefix), entry: Some(entry), section: None }
176182
}
177-
pub fn prefix(&self) -> u8 {
183+
pub fn prefix(&self) -> Option<u8> {
178184
self.prefix
179185
}
180-
pub fn entry(&self) -> u8 {
186+
pub fn entry(&self) -> Option<u8> {
181187
self.entry
182188
}
189+
pub fn section(&self) -> Option<Symbol> {
190+
self.section
191+
}
183192
}
184193

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

compiler/rustc_session/src/config.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3330,23 +3330,29 @@ impl DumpMonoStatsFormat {
33303330

33313331
/// `-Z patchable-function-entry` representation - how many nops to put before and after function
33323332
/// entry.
3333-
#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
3333+
#[derive(Clone, PartialEq, Hash, Debug, Default)]
33343334
pub struct PatchableFunctionEntry {
33353335
/// Nops before the entry
33363336
prefix: u8,
33373337
/// Nops after the entry
33383338
entry: u8,
3339+
/// An optional section name to record the entry location
3340+
section: Option<String>,
33393341
}
33403342

33413343
impl PatchableFunctionEntry {
3342-
pub fn from_total_and_prefix_nops(
3344+
pub fn from_parts(
33433345
total_nops: u8,
33443346
prefix_nops: u8,
3347+
section: Option<String>,
33453348
) -> Option<PatchableFunctionEntry> {
33463349
if total_nops < prefix_nops {
33473350
None
3351+
// Section name cannot contain null characters.
3352+
} else if section.as_ref().map(|x| x.contains('\0')).unwrap_or(false) {
3353+
None
33483354
} else {
3349-
Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops })
3355+
Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops, section })
33503356
}
33513357
}
33523358
pub fn prefix(&self) -> u8 {
@@ -3355,6 +3361,9 @@ impl PatchableFunctionEntry {
33553361
pub fn entry(&self) -> u8 {
33563362
self.entry
33573363
}
3364+
pub fn section(&self) -> Option<&str> {
3365+
self.section.as_ref().map(|x| x.as_str())
3366+
}
33583367
}
33593368

33603369
/// `-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
@@ -784,7 +784,7 @@ mod desc {
784784
pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`";
785785
pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`";
786786
pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`";
787-
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)";
787+
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";
788788
pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy;
789789
pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
790790
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'";
@@ -1204,20 +1204,24 @@ pub mod parse {
12041204
) -> bool {
12051205
let mut total_nops = 0;
12061206
let mut prefix_nops = 0;
1207+
let mut section = None;
12071208

12081209
if !parse_number(&mut total_nops, v) {
1209-
let parts = v.and_then(|v| v.split_once(',')).unzip();
1210-
if !parse_number(&mut total_nops, parts.0) {
1210+
let parts: Vec<_> = v.unwrap_or("").split(',').collect();
1211+
if parts.len() < 2 || parts.len() > 3 {
12111212
return false;
12121213
}
1213-
if !parse_number(&mut prefix_nops, parts.1) {
1214+
1215+
if !parse_number(&mut total_nops, Some(parts[0])) {
1216+
return false;
1217+
}
1218+
if !parse_number(&mut prefix_nops, Some(parts[1])) {
12141219
return false;
12151220
}
1221+
section = parts.get(2).map(|x| x.to_string());
12161222
}
12171223

1218-
if let Some(pfe) =
1219-
PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops)
1220-
{
1224+
if let Some(pfe) = PatchableFunctionEntry::from_parts(total_nops, prefix_nops, section) {
12211225
*slot = pfe;
12221226
return true;
12231227
}

0 commit comments

Comments
 (0)