Skip to content

Commit acc6bcd

Browse files
authored
Support alternative sections for patchable function entries (#131230)
With -fpatchable-function-entry (or the patchable_function_entry function attribute), we emit records of patchable entry locations to the __patchable_function_entries section. Add an additional parameter to the command line option that allows one to specify a different default section name for the records, and an identical parameter to the function attribute that allows one to override the section used. The main use case for this change is the Linux kernel using prefix NOPs for ftrace, and thus depending on__patchable_function_entries to locate traceable functions. Functions that are not traceable currently disable entry NOPs using the function attribute, but this creates a compatibility issue with -fsanitize=kcfi, which expects all indirectly callable functions to have a type hash prefix at the same offset from the function entry. Adding a section parameter would allow the kernel to distinguish between traceable and non-traceable functions by adding entry records to separate sections while maintaining a stable function prefix layout for all functions. LKML discussion: https://lore.kernel.org/lkml/Y1QEzk%[email protected]/
1 parent 76fa953 commit acc6bcd

15 files changed

+146
-18
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,8 @@ def PatchableFunctionEntry
936936
"riscv64", "x86", "x86_64", "ppc", "ppc64"]>> {
937937
let Spellings = [GCC<"patchable_function_entry">];
938938
let Subjects = SubjectList<[Function, ObjCMethod]>;
939-
let Args = [UnsignedArgument<"Count">, DefaultIntArgument<"Offset", 0>];
939+
let Args = [UnsignedArgument<"Count">, DefaultIntArgument<"Offset", 0>,
940+
StringArgument<"Section", /* optional */ 1>];
940941
let Documentation = [PatchableFunctionEntryDocs];
941942
}
942943

clang/include/clang/Basic/AttrDocs.td

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6502,10 +6502,12 @@ only N==1 is supported.
65026502
def PatchableFunctionEntryDocs : Documentation {
65036503
let Category = DocCatFunction;
65046504
let Content = [{
6505-
``__attribute__((patchable_function_entry(N,M)))`` is used to generate M NOPs
6506-
before the function entry and N-M NOPs after the function entry. This attribute
6507-
takes precedence over the command line option ``-fpatchable-function-entry=N,M``.
6508-
``M`` defaults to 0 if omitted.
6505+
``__attribute__((patchable_function_entry(N,M,Section)))`` is used to generate M
6506+
NOPs before the function entry and N-M NOPs after the function entry, with a record of
6507+
the entry stored in section ``Section``. This attribute takes precedence over the
6508+
command line option ``-fpatchable-function-entry=N,M,Section``. ``M`` defaults to 0
6509+
if omitted.``Section`` defaults to the ``-fpatchable-function-entry`` section name if
6510+
set, or to ``__patchable_function_entries`` otherwise.
65096511

65106512
This attribute is only supported on
65116513
aarch64/aarch64-be/loongarch32/loongarch64/riscv32/riscv64/i386/x86-64/ppc/ppc64 targets.

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
281281
/// -fprofile-generate, and -fcs-profile-generate.
282282
std::string InstrProfileOutput;
283283

284+
/// Name of the patchable function entry section with
285+
/// -fpatchable-function-entry.
286+
std::string PatchableFunctionEntrySection;
287+
284288
/// Name of the profile file to use with -fprofile-sample-use.
285289
std::string SampleProfileFile;
286290

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3550,6 +3550,10 @@ def err_conflicting_codeseg_attribute : Error<
35503550
def warn_duplicate_codeseg_attribute : Warning<
35513551
"duplicate code segment specifiers">, InGroup<Section>;
35523552

3553+
def err_attribute_patchable_function_entry_invalid_section
3554+
: Error<"section argument to 'patchable_function_entry' attribute is not "
3555+
"valid for this target: %0">;
3556+
35533557
def err_anonymous_property: Error<
35543558
"anonymous property is not supported">;
35553559
def err_property_is_variably_modified : Error<

clang/include/clang/Driver/Options.td

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3759,10 +3759,16 @@ defm pascal_strings : BoolFOption<"pascal-strings",
37593759
// Note: This flag has different semantics in the driver and in -cc1. The driver accepts -fpatchable-function-entry=M,N
37603760
// and forwards it to -cc1 as -fpatchable-function-entry=M and -fpatchable-function-entry-offset=N. In -cc1, both flags
37613761
// are treated as a single integer.
3762-
def fpatchable_function_entry_EQ : Joined<["-"], "fpatchable-function-entry=">, Group<f_Group>,
3763-
Visibility<[ClangOption, CC1Option]>,
3764-
MetaVarName<"<N,M>">, HelpText<"Generate M NOPs before function entry and N-M NOPs after function entry">,
3765-
MarshallingInfoInt<CodeGenOpts<"PatchableFunctionEntryCount">>;
3762+
def fpatchable_function_entry_EQ
3763+
: Joined<["-"], "fpatchable-function-entry=">,
3764+
Group<f_Group>,
3765+
Visibility<[ClangOption, CC1Option]>,
3766+
MetaVarName<"<N,M,Section>">,
3767+
HelpText<"Generate M NOPs before function entry and N-M NOPs after "
3768+
"function entry. "
3769+
"If section is specified, use it instead of "
3770+
"__patchable_function_entries.">,
3771+
MarshallingInfoInt<CodeGenOpts<"PatchableFunctionEntryCount">>;
37663772
def fms_hotpatch : Flag<["-"], "fms-hotpatch">, Group<f_Group>,
37673773
Visibility<[ClangOption, CC1Option, CLOption]>,
37683774
HelpText<"Ensure that all functions can be hotpatched at runtime">,
@@ -7593,6 +7599,11 @@ def fpatchable_function_entry_offset_EQ
75937599
: Joined<["-"], "fpatchable-function-entry-offset=">, MetaVarName<"<M>">,
75947600
HelpText<"Generate M NOPs before function entry">,
75957601
MarshallingInfoInt<CodeGenOpts<"PatchableFunctionEntryOffset">>;
7602+
def fpatchable_function_entry_section_EQ
7603+
: Joined<["-"], "fpatchable-function-entry-section=">,
7604+
MetaVarName<"<Section>">,
7605+
HelpText<"Use Section instead of __patchable_function_entries">,
7606+
MarshallingInfoString<CodeGenOpts<"PatchableFunctionEntrySection">>;
75967607
def fprofile_instrument_EQ : Joined<["-"], "fprofile-instrument=">,
75977608
HelpText<"Enable PGO instrumentation">, Values<"none,clang,llvm,csllvm">,
75987609
NormalizedValuesScope<"CodeGenOptions">,

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,18 +965,24 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
965965
}
966966

967967
unsigned Count, Offset;
968+
StringRef Section;
968969
if (const auto *Attr =
969970
D ? D->getAttr<PatchableFunctionEntryAttr>() : nullptr) {
970971
Count = Attr->getCount();
971972
Offset = Attr->getOffset();
973+
Section = Attr->getSection();
972974
} else {
973975
Count = CGM.getCodeGenOpts().PatchableFunctionEntryCount;
974976
Offset = CGM.getCodeGenOpts().PatchableFunctionEntryOffset;
975977
}
978+
if (Section.empty())
979+
Section = CGM.getCodeGenOpts().PatchableFunctionEntrySection;
976980
if (Count && Offset <= Count) {
977981
Fn->addFnAttr("patchable-function-entry", std::to_string(Count - Offset));
978982
if (Offset)
979983
Fn->addFnAttr("patchable-function-prefix", std::to_string(Offset));
984+
if (!Section.empty())
985+
Fn->addFnAttr("patchable-function-entry-section", Section);
980986
}
981987
// Instruct that functions for COFF/CodeView targets should start with a
982988
// patchable instruction, but only on x86/x64. Don't forward this to ARM/ARM64

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6917,8 +6917,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
69176917
D.Diag(diag::err_drv_unsupported_opt_for_target)
69186918
<< A->getAsString(Args) << TripleStr;
69196919
else if (S.consumeInteger(10, Size) ||
6920-
(!S.empty() && (!S.consume_front(",") ||
6921-
S.consumeInteger(10, Offset) || !S.empty())))
6920+
(!S.empty() &&
6921+
(!S.consume_front(",") || S.consumeInteger(10, Offset))) ||
6922+
(!S.empty() && (!S.consume_front(",") || S.empty())))
69226923
D.Diag(diag::err_drv_invalid_argument_to_option)
69236924
<< S0 << A->getOption().getName();
69246925
else if (Size < Offset)
@@ -6927,6 +6928,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
69276928
CmdArgs.push_back(Args.MakeArgString(A->getSpelling() + Twine(Size)));
69286929
CmdArgs.push_back(Args.MakeArgString(
69296930
"-fpatchable-function-entry-offset=" + Twine(Offset)));
6931+
if (!S.empty())
6932+
CmdArgs.push_back(
6933+
Args.MakeArgString("-fpatchable-function-entry-section=" + S));
69306934
}
69316935
}
69326936

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5833,9 +5833,10 @@ static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D,
58335833
return;
58345834
}
58355835
uint32_t Count = 0, Offset = 0;
5836+
StringRef Section;
58365837
if (!S.checkUInt32Argument(AL, AL.getArgAsExpr(0), Count, 0, true))
58375838
return;
5838-
if (AL.getNumArgs() == 2) {
5839+
if (AL.getNumArgs() >= 2) {
58395840
Expr *Arg = AL.getArgAsExpr(1);
58405841
if (!S.checkUInt32Argument(AL, Arg, Offset, 1, true))
58415842
return;
@@ -5845,8 +5846,25 @@ static void handlePatchableFunctionEntryAttr(Sema &S, Decl *D,
58455846
return;
58465847
}
58475848
}
5848-
D->addAttr(::new (S.Context)
5849-
PatchableFunctionEntryAttr(S.Context, AL, Count, Offset));
5849+
if (AL.getNumArgs() == 3) {
5850+
SourceLocation LiteralLoc;
5851+
if (!S.checkStringLiteralArgumentAttr(AL, 2, Section, &LiteralLoc))
5852+
return;
5853+
if (llvm::Error E = S.isValidSectionSpecifier(Section)) {
5854+
S.Diag(LiteralLoc,
5855+
diag::err_attribute_patchable_function_entry_invalid_section)
5856+
<< toString(std::move(E));
5857+
return;
5858+
}
5859+
if (Section.empty()) {
5860+
S.Diag(LiteralLoc,
5861+
diag::err_attribute_patchable_function_entry_invalid_section)
5862+
<< "section must not be empty";
5863+
return;
5864+
}
5865+
}
5866+
D->addAttr(::new (S.Context) PatchableFunctionEntryAttr(S.Context, AL, Count,
5867+
Offset, Section));
58505868
}
58515869

58525870
static void handleBuiltinAliasAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %clang_cc1 -triple aarch64 -emit-llvm %s -o - | FileCheck --check-prefixes=COMMON,NODEFAULT %s
2+
// RUN: %clang_cc1 -triple x86_64 -emit-llvm %s -fpatchable-function-entry=1 -fpatchable-function-entry-section=__default_section -o - | FileCheck --check-prefixes=COMMON,DEFAULT %s
3+
4+
// COMMON: define{{.*}} void @f0() #0
5+
__attribute__((patchable_function_entry(0))) void f0(void) {}
6+
7+
// COMMON: define{{.*}} void @f00() #0
8+
__attribute__((patchable_function_entry(0, 0, "__unused_section"))) void f00(void) {}
9+
10+
// COMMON: define{{.*}} void @f2() #1
11+
__attribute__((patchable_function_entry(2))) void f2(void) {}
12+
13+
// COMMON: define{{.*}} void @f20() #2
14+
__attribute__((patchable_function_entry(2, 0, "__attr_section"))) void f20(void) {}
15+
16+
// COMMON: define{{.*}} void @f44() #3
17+
__attribute__((patchable_function_entry(4, 4))) void f44(void) {}
18+
19+
// COMMON: define{{.*}} void @f52() #4
20+
__attribute__((patchable_function_entry(5, 2, "__attr_section"))) void f52(void) {}
21+
22+
// OPT: define{{.*}} void @f() #5
23+
void f(void) {}
24+
25+
/// No need to emit "patchable-function-entry" and thus also "patchable-function-entry-section"
26+
// COMMON: attributes #0 = { {{.*}}
27+
// COMMON-NOT: "patchable-function-entry-section"
28+
29+
// NODEFAULT: attributes #1 = { {{.*}} "patchable-function-entry"="2"
30+
// NODEFAULT-NOT: "patchable-function-entry-section"
31+
// DEFAULT: attributes #1 = { {{.*}} "patchable-function-entry"="2" "patchable-function-entry-section"="__default_section"
32+
33+
// COMMON: attributes #2 = { {{.*}} "patchable-function-entry"="2" "patchable-function-entry-section"="__attr_section"
34+
35+
// NODEFAULT: attributes #3 = { {{.*}} "patchable-function-entry"="0" "patchable-function-prefix"="4"
36+
// NODEFAULT-NOT: "patchable-function-entry-section"
37+
// DEFAULT: attributes #3 = { {{.*}} "patchable-function-entry"="0" "patchable-function-entry-section"="__default_section" "patchable-function-prefix"="4"
38+
39+
// COMMON: attributes #4 = { {{.*}} "patchable-function-entry"="3" "patchable-function-entry-section"="__attr_section" "patchable-function-prefix"="2"
40+
41+
// DEFAULT: attributes #5 = { {{.*}} "patchable-function-entry"="1" "patchable-function-entry-section"="__default_section"

clang/test/Driver/fpatchable-function-entry.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
// RUN: %clang --target=aarch64 -fsyntax-only %s -fpatchable-function-entry=2,1 -c -### 2>&1 | FileCheck --check-prefix=21 %s
1616
// 21: "-fpatchable-function-entry=2" "-fpatchable-function-entry-offset=1"
1717

18+
// RUN: %clang --target=aarch64 -fsyntax-only %s -fpatchable-function-entry=1,1,__section_name -c -### 2>&1 | FileCheck --check-prefix=SECTION %s
19+
// SECTION: "-fpatchable-function-entry=1" "-fpatchable-function-entry-offset=1" "-fpatchable-function-entry-section=__section_name"
20+
1821
// RUN: not %clang --target=powerpc64-ibm-aix-xcoff -fsyntax-only %s -fpatchable-function-entry=1 2>&1 | FileCheck --check-prefix=AIX64 %s
1922
// AIX64: error: unsupported option '-fpatchable-function-entry=1' for target 'powerpc64-ibm-aix-xcoff'
2023

clang/test/Sema/patchable-function-entry-attr.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,15 @@
33
// expected-error@+1 {{'patchable_function_entry' attribute takes at least 1 argument}}
44
__attribute__((patchable_function_entry)) void f(void);
55

6-
// expected-error@+1 {{'patchable_function_entry' attribute takes no more than 2 arguments}}
6+
// expected-error@+1 {{expected string literal as argument of 'patchable_function_entry' attribute}}
77
__attribute__((patchable_function_entry(0, 0, 0))) void f(void);
88

9+
// expected-error@+1 {{section argument to 'patchable_function_entry' attribute is not valid for this target}}
10+
__attribute__((patchable_function_entry(0, 0, ""))) void f(void);
11+
12+
// expected-error@+1 {{'patchable_function_entry' attribute takes no more than 3 arguments}}
13+
__attribute__((patchable_function_entry(0, 0, "__section", 0))) void f(void);
14+
915
// expected-error@+1 {{'patchable_function_entry' attribute requires a non-negative integral compile time constant expression}}
1016
__attribute__((patchable_function_entry(-1))) void f(void);
1117

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4602,7 +4602,13 @@ void AsmPrinter::emitPatchableFunctionEntries() {
46024602
if (TM.getTargetTriple().isOSBinFormatELF()) {
46034603
auto Flags = ELF::SHF_WRITE | ELF::SHF_ALLOC;
46044604
const MCSymbolELF *LinkedToSym = nullptr;
4605-
StringRef GroupName;
4605+
StringRef GroupName, SectionName;
4606+
4607+
if (F.hasFnAttribute("patchable-function-entry-section"))
4608+
SectionName = F.getFnAttribute("patchable-function-entry-section")
4609+
.getValueAsString();
4610+
if (SectionName.empty())
4611+
SectionName = "__patchable_function_entries";
46064612

46074613
// GNU as < 2.35 did not support section flag 'o'. GNU ld < 2.36 did not
46084614
// support mixed SHF_LINK_ORDER and non-SHF_LINK_ORDER sections.
@@ -4615,8 +4621,8 @@ void AsmPrinter::emitPatchableFunctionEntries() {
46154621
LinkedToSym = cast<MCSymbolELF>(CurrentFnSym);
46164622
}
46174623
OutStreamer->switchSection(OutContext.getELFSection(
4618-
"__patchable_function_entries", ELF::SHT_PROGBITS, Flags, 0, GroupName,
4619-
F.hasComdat(), MCSection::NonUniqueID, LinkedToSym));
4624+
SectionName, ELF::SHT_PROGBITS, Flags, 0, GroupName, F.hasComdat(),
4625+
MCSection::NonUniqueID, LinkedToSym));
46204626
emitAlignment(Align(PointerSize));
46214627
OutStreamer->emitSymbolValue(CurrentPatchableFunctionEntrySym, PointerSize);
46224628
}

llvm/lib/IR/Verifier.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2409,6 +2409,11 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
24092409

24102410
checkUnsignedBaseTenFuncAttr(Attrs, "patchable-function-prefix", V);
24112411
checkUnsignedBaseTenFuncAttr(Attrs, "patchable-function-entry", V);
2412+
if (Attrs.hasFnAttr("patchable-function-entry-section"))
2413+
Check(!Attrs.getFnAttr("patchable-function-entry-section")
2414+
.getValueAsString()
2415+
.empty(),
2416+
"\"patchable-function-entry-section\" must not be empty");
24122417
checkUnsignedBaseTenFuncAttr(Attrs, "warn-stack-size", V);
24132418

24142419
if (auto A = Attrs.getFnAttr("sign-return-address"); A.isValid()) {

llvm/test/CodeGen/X86/patchable-function-entry.ll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,16 @@ define void @f3_2() "patchable-function-entry"="1" "patchable-function-prefix"="
9898
%frame = alloca i8, i32 16
9999
ret void
100100
}
101+
102+
define void @s1() "patchable-function-entry"="1" "patchable-function-entry-section"=".entries" {
103+
; CHECK-LABEL: s1:
104+
; CHECK-NEXT: .Lfunc_begin6:
105+
; CHECK: nop
106+
; CHECK-NEXT: ret
107+
; CHECK: .section .entries,"awo",@progbits,s1{{$}}
108+
; X86: .p2align 2
109+
; X86-NEXT: .long .Lfunc_begin6
110+
; X64: .p2align 3
111+
; X64-NEXT: .quad .Lfunc_begin6
112+
ret void
113+
}

llvm/test/Verifier/invalid-patchable-function-entry.ll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ define void @g() "patchable-function-prefix" { ret void }
1919
define void @ga() "patchable-function-prefix"="a" { ret void }
2020
define void @g_1() "patchable-function-prefix"="-1" { ret void }
2121
define void @g3comma() "patchable-function-prefix"="3," { ret void }
22+
23+
; CHECK: "patchable-function-entry-section" must not be empty
24+
25+
define void @s1() "patchable-function-entry"="1" "patchable-function-entry-section" { ret void }

0 commit comments

Comments
 (0)