Skip to content

Commit 1728405

Browse files
authored
[LLD][ELF][RISCV][Zicfilp] Handle .note.gnu.property sections for Zicfilp/Zicfiss features (#127193)
+ When all relocatable files contain a `.note.gnu.property` section (with `NT_GNU_PROPERTY_TYPE_0` notes) which contains a `GNU_PROPERTY_RISCV_FEATURE_1_AND` property in which the `GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED`/`GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG`/`GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_SS` bit is set: + The output file will contain a `.note.gnu.property` section with the bit set + A `PT_GNU_PROPERTY` program header is created to encompass the `.note.gnu.property` section + If `-z zicfilp-unlabeled-report=[warning|error]`/`-z zicfilp-func-sig-report=[warning|error]`/`-z zicfiss-report=[warning|error]` is specified, the linker will report a warning or error for any relocatable file lacking the feature bit RISC-V Zicfilp/Zicfiss features indicate their adoptions as bits in the `.note.gnu.property` section of ELF files. This patch enables LLD to process the information correctly by parsing, checking and merging the bits from all input ELF files and writing the merged result to the output ELF file. These feature bits are encoded as a mask in each input ELF files and intended to be "and"-ed together to check that all input files support a particular feature. For RISC-V Zicfilp features, there are 2 conflicting bits allocated: `GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED` and `GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG`. They represent the adoption of the forward edge protection of control-flow integrity with the "unlabeled" or "func-sig" policy. Since these 2 policies conflicts with each other, these 2 bits also conflict with each other. This patch adds the `-z zicfilp-unlabeled-report=[none|warning|error]` and `-z zicfilp-func-sig-report=[none|warning|error]` commandline options to make LLD report files that do not have the expected bits toggled on. For RISC-V Zicfiss feature, there's only one bit allocated: `GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS`. This bit indicates that the ELF file supports Zicfiss-based shadow stack. This patch adds the `-z zicfiss-report=[none|warning|error]` commandline option to make LLD report files that do not have the expected bit toggled on. The adoption of the `.note.gnu.property` section for RISC-V targets can be found in the psABI PR <riscv-non-isa/riscv-elf-psabi-doc#417> (`CFI_LP_UNLABELED` and `CFI_SS`) and PR <riscv-non-isa/riscv-elf-psabi-doc#434> (`CFI_LP_FUNC_SIG`).
1 parent 0a1fdbe commit 1728405

File tree

7 files changed

+695
-16
lines changed

7 files changed

+695
-16
lines changed

lld/ELF/Config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ struct Config {
235235
ReportPolicy zGcsReport = ReportPolicy::None;
236236
ReportPolicy zGcsReportDynamic = ReportPolicy::None;
237237
ReportPolicy zExecuteOnlyReport = ReportPolicy::None;
238+
ReportPolicy zZicfilpUnlabeledReport = ReportPolicy::None;
239+
ReportPolicy zZicfilpFuncSigReport = ReportPolicy::None;
240+
ReportPolicy zZicfissReport = ReportPolicy::None;
238241
bool ltoBBAddrMap;
239242
llvm::StringRef ltoBasicBlockSections;
240243
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;

lld/ELF/Driver.cpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,18 @@ static void checkOptions(Ctx &ctx) {
419419
<< "--pcrel-optimize is only supported on PowerPC64 targets";
420420
}
421421

422-
if (ctx.arg.relaxGP && ctx.arg.emachine != EM_RISCV)
423-
ErrAlways(ctx) << "--relax-gp is only supported on RISC-V targets";
422+
if (ctx.arg.emachine != EM_RISCV) {
423+
if (ctx.arg.relaxGP)
424+
ErrAlways(ctx) << "--relax-gp is only supported on RISC-V targets";
425+
if (ctx.arg.zZicfilpUnlabeledReport != ReportPolicy::None)
426+
ErrAlways(ctx) << "-z zicfilip-unlabeled-report is only supported on "
427+
"RISC-V targets";
428+
if (ctx.arg.zZicfilpFuncSigReport != ReportPolicy::None)
429+
ErrAlways(ctx) << "-z zicfilip-func-sig-report is only supported on "
430+
"RISC-V targets";
431+
if (ctx.arg.zZicfissReport != ReportPolicy::None)
432+
ErrAlways(ctx) << "-z zicfiss-report is only supported on RISC-V targets";
433+
}
424434

425435
if (ctx.arg.emachine != EM_386 && ctx.arg.emachine != EM_X86_64 &&
426436
ctx.arg.zCetReport != ReportPolicy::None)
@@ -1635,7 +1645,11 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
16351645
std::make_pair("execute-only-report", &ctx.arg.zExecuteOnlyReport),
16361646
std::make_pair("gcs-report", &ctx.arg.zGcsReport),
16371647
std::make_pair("gcs-report-dynamic", &ctx.arg.zGcsReportDynamic),
1638-
std::make_pair("pauth-report", &ctx.arg.zPauthReport)};
1648+
std::make_pair("pauth-report", &ctx.arg.zPauthReport),
1649+
std::make_pair("zicfilp-unlabeled-report",
1650+
&ctx.arg.zZicfilpUnlabeledReport),
1651+
std::make_pair("zicfilp-func-sig-report", &ctx.arg.zZicfilpFuncSigReport),
1652+
std::make_pair("zicfiss-report", &ctx.arg.zZicfissReport)};
16391653
bool hasGcsReportDynamic = false;
16401654
for (opt::Arg *arg : args.filtered(OPT_z)) {
16411655
std::pair<StringRef, StringRef> option =
@@ -2829,9 +2843,12 @@ static void redirectSymbols(Ctx &ctx, ArrayRef<WrappedSymbol> wrapped) {
28292843
// For AArch64 PAuth-enabled object files, the core info of all of them must
28302844
// match. Missing info for some object files with matching info for remaining
28312845
// ones can be allowed (see -z pauth-report).
2846+
//
2847+
// RISC-V Zicfilp/Zicfiss extension also use the same mechanism to record
2848+
// enabled features in the GNU_PROPERTY_RISCV_FEATURE_1_AND bit mask.
28322849
static void readSecurityNotes(Ctx &ctx) {
28332850
if (ctx.arg.emachine != EM_386 && ctx.arg.emachine != EM_X86_64 &&
2834-
ctx.arg.emachine != EM_AARCH64)
2851+
ctx.arg.emachine != EM_AARCH64 && ctx.arg.emachine != EM_RISCV)
28352852
return;
28362853

28372854
ctx.arg.andFeatures = -1;
@@ -2883,6 +2900,33 @@ static void readSecurityNotes(Ctx &ctx) {
28832900
<< ": -z cet-report: file does not have "
28842901
"GNU_PROPERTY_X86_FEATURE_1_SHSTK property";
28852902

2903+
if (ctx.arg.emachine == EM_RISCV) {
2904+
reportUnless(ctx.arg.zZicfilpUnlabeledReport,
2905+
features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
2906+
<< f
2907+
<< ": -z zicfilp-unlabeled-report: file does not have "
2908+
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED property";
2909+
2910+
reportUnless(ctx.arg.zZicfilpFuncSigReport,
2911+
features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG)
2912+
<< f
2913+
<< ": -z zicfilp-func-sig-report: file does not have "
2914+
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG property";
2915+
2916+
if ((features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) &&
2917+
(features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG))
2918+
Err(ctx) << f
2919+
<< ": file has conflicting properties: "
2920+
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED and "
2921+
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG";
2922+
2923+
reportUnless(ctx.arg.zZicfissReport,
2924+
features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
2925+
<< f
2926+
<< ": -z zicfiss-report: file does not have "
2927+
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS property";
2928+
}
2929+
28862930
if (ctx.arg.zForceBti && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
28872931
features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
28882932
if (ctx.arg.zBtiReport == ReportPolicy::None)

lld/ELF/InputFiles.cpp

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f,
968968
}
969969
// Read the following info from the .note.gnu.property section and write it to
970970
// the corresponding fields in `ObjFile`:
971-
// - Feature flags (32 bits) representing x86 or AArch64 features for
971+
// - Feature flags (32 bits) representing x86, AArch64 or RISC-V features for
972972
// hardware-assisted call flow control;
973973
// - AArch64 PAuth ABI core info (16 bytes).
974974
template <class ELFT>
@@ -977,6 +977,22 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
977977
using Elf_Nhdr = typename ELFT::Nhdr;
978978
using Elf_Note = typename ELFT::Note;
979979

980+
uint32_t featureAndType;
981+
switch (ctx.arg.emachine) {
982+
case EM_386:
983+
case EM_X86_64:
984+
featureAndType = GNU_PROPERTY_X86_FEATURE_1_AND;
985+
break;
986+
case EM_AARCH64:
987+
featureAndType = GNU_PROPERTY_AARCH64_FEATURE_1_AND;
988+
break;
989+
case EM_RISCV:
990+
featureAndType = GNU_PROPERTY_RISCV_FEATURE_1_AND;
991+
break;
992+
default:
993+
return;
994+
}
995+
980996
ArrayRef<uint8_t> data = sec.content();
981997
auto err = [&](const uint8_t *place) -> ELFSyncStream {
982998
auto diag = Err(ctx);
@@ -997,10 +1013,6 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
9971013
continue;
9981014
}
9991015

1000-
uint32_t featureAndType = ctx.arg.emachine == EM_AARCH64
1001-
? GNU_PROPERTY_AARCH64_FEATURE_1_AND
1002-
: GNU_PROPERTY_X86_FEATURE_1_AND;
1003-
10041016
// Read a body of a NOTE record, which consists of type-length-value fields.
10051017
ArrayRef<uint8_t> desc = note.getDesc(sec.addralign);
10061018
const uint8_t *base = sec.content().data();
@@ -1064,9 +1076,9 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
10641076
}
10651077

10661078
// Object files that use processor features such as Intel Control-Flow
1067-
// Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
1068-
// .note.gnu.property section containing a bitfield of feature bits like the
1069-
// GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
1079+
// Enforcement (CET), AArch64 Branch Target Identification BTI or RISC-V
1080+
// Zicfilp/Zicfiss extensions, use a .note.gnu.property section containing
1081+
// a bitfield of feature bits like the GNU_PROPERTY_X86_FEATURE_1_IBT flag.
10701082
//
10711083
// Since we merge bitmaps from multiple object files to create a new
10721084
// .note.gnu.property containing a single AND'ed bitmap, we discard an input

lld/ELF/SyntheticSections.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,15 +322,28 @@ GnuPropertySection::GnuPropertySection(Ctx &ctx)
322322
ctx.arg.wordsize) {}
323323

324324
void GnuPropertySection::writeTo(uint8_t *buf) {
325+
uint32_t featureAndType;
326+
switch (ctx.arg.emachine) {
327+
case EM_386:
328+
case EM_X86_64:
329+
featureAndType = GNU_PROPERTY_X86_FEATURE_1_AND;
330+
break;
331+
case EM_AARCH64:
332+
featureAndType = GNU_PROPERTY_AARCH64_FEATURE_1_AND;
333+
break;
334+
case EM_RISCV:
335+
featureAndType = GNU_PROPERTY_RISCV_FEATURE_1_AND;
336+
break;
337+
default:
338+
llvm_unreachable(
339+
"target machine does not support .note.gnu.property section");
340+
}
341+
325342
write32(ctx, buf, 4); // Name size
326343
write32(ctx, buf + 4, getSize() - 16); // Content size
327344
write32(ctx, buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
328345
memcpy(buf + 12, "GNU", 4); // Name string
329346

330-
uint32_t featureAndType = ctx.arg.emachine == EM_AARCH64
331-
? GNU_PROPERTY_AARCH64_FEATURE_1_AND
332-
: GNU_PROPERTY_X86_FEATURE_1_AND;
333-
334347
unsigned offset = 16;
335348
if (ctx.arg.andFeatures != 0) {
336349
write32(ctx, buf + offset + 0, featureAndType); // Feature type
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# REQUIRES: riscv
2+
## Test the ZICFILP func-sig feature.
3+
## To lift maintenance burden, most tests are conducted only with 64-bit RISC-V
4+
## Naming convention: *-s.s files enables ZICFILP func-sig.
5+
# RUN: rm -rf %t && split-file %s %t && cd %t
6+
# RUN: llvm-mc --filetype=obj --triple=riscv32 rv32-f1-s.s -o rv32-f1-s.o
7+
# RUN: llvm-mc --filetype=obj --triple=riscv32 rv32-f2-s.s -o rv32-f2-s.o
8+
# RUN: llvm-mc --filetype=obj --triple=riscv32 rv32-f3-s.s -o rv32-f3-s.o
9+
10+
# RUN: llvm-mc --filetype=obj --triple=riscv64 f1-s.s -o f1-s.o
11+
# RUN: llvm-mc --filetype=obj --triple=riscv64 f2.s -o f2.o
12+
# RUN: llvm-mc --filetype=obj --triple=riscv64 f2-s.s -o f2-s.o
13+
# RUN: llvm-mc --filetype=obj --triple=riscv64 f3.s -o f3.o
14+
# RUN: llvm-mc --filetype=obj --triple=riscv64 f3-s.s -o f3-s.o
15+
16+
## ZICFILP-func-sig should be enabled when it's enabled in all inputs
17+
# RUN: ld.lld rv32-f1-s.o rv32-f2-s.o rv32-f3-s.o -o out.rv32 --fatal-warnings
18+
# RUN: llvm-readelf -n out.rv32 | FileCheck --check-prefix=ZICFILP %s
19+
# RUN: ld.lld f1-s.o f2-s.o f3-s.o -o out --fatal-warnings
20+
# RUN: llvm-readelf -n out | FileCheck --check-prefix=ZICFILP %s
21+
# RUN: ld.lld f1-s.o f3-s.o --shared -o out.so --fatal-warnings
22+
# RUN: llvm-readelf -n out.so | FileCheck --check-prefix=ZICFILP %s
23+
# ZICFILP: Properties: RISC-V feature: ZICFILP-func-sig
24+
25+
## ZICFILP-func-sig should not be enabled if it's not enabled in at least one
26+
## input
27+
# RUN: ld.lld f1-s.o f2.o f3-s.o -o out.no --fatal-warnings
28+
# RUN: llvm-readelf -n out.no | count 0
29+
# RUN: ld.lld f2-s.o f3.o --shared -o out.no.so --fatal-warnings
30+
# RUN: llvm-readelf -n out.no.so | count 0
31+
32+
## zicfilp-func-sig-report should report any input files that don't have the
33+
## ZICFILP-func-sig property
34+
# RUN: ld.lld f1-s.o f2.o f3-s.o -z zicfilp-func-sig-report=warning 2>&1 | FileCheck --check-prefix=REPORT-WARN %s
35+
# RUN: not ld.lld f2-s.o f3.o --shared -z zicfilp-func-sig-report=error 2>&1 | FileCheck --check-prefix=REPORT-ERROR %s
36+
# RUN: ld.lld f1-s.o f2-s.o f3-s.o -z zicfilp-func-sig-report=warning 2>&1 | count 0
37+
# REPORT-WARN: warning: f2.o: -z zicfilp-func-sig-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG property
38+
# REPORT-ERROR: error: f3.o: -z zicfilp-func-sig-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG property
39+
40+
## An invalid -z zicfilp-func-sig-report option should give an error
41+
# RUN: not ld.lld f2-s.o -z zicfilp-func-sig-report=x 2>&1 | FileCheck --check-prefix=INVALID %s
42+
# INVALID: error: unknown -z zicfilp-func-sig-report= value: x
43+
44+
#--- rv32-f1-s.s
45+
.section ".note.gnu.property", "a"
46+
.balign 4
47+
.4byte 4
48+
.4byte (ndesc_end - ndesc_begin)
49+
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
50+
.asciz "GNU"
51+
ndesc_begin:
52+
.balign 4
53+
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
54+
.4byte 4
55+
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
56+
.balign 4
57+
ndesc_end:
58+
59+
.text
60+
.globl _start
61+
.type f1,%function
62+
f1:
63+
call f2
64+
ret
65+
66+
#--- f1-s.s
67+
.section ".note.gnu.property", "a"
68+
.balign 8
69+
.4byte 4
70+
.4byte (ndesc_end - ndesc_begin)
71+
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
72+
.asciz "GNU"
73+
ndesc_begin:
74+
.balign 8
75+
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
76+
.4byte 4
77+
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
78+
.balign 8
79+
ndesc_end:
80+
81+
.text
82+
.globl _start
83+
.type f1,%function
84+
f1:
85+
call f2
86+
ret
87+
88+
#--- f2.s
89+
.text
90+
.globl f2
91+
.type f2,@function
92+
f2:
93+
.globl f3
94+
.type f3, @function
95+
call f3
96+
ret
97+
98+
#--- rv32-f2-s.s
99+
.section ".note.gnu.property", "a"
100+
.balign 4
101+
.4byte 4
102+
.4byte (ndesc_end - ndesc_begin)
103+
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
104+
.asciz "GNU"
105+
ndesc_begin:
106+
.balign 4
107+
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
108+
.4byte 4
109+
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
110+
.balign 4
111+
ndesc_end:
112+
113+
.text
114+
.globl f2
115+
.type f2,@function
116+
f2:
117+
.globl f3
118+
.type f3, @function
119+
call f3
120+
ret
121+
122+
#--- f2-s.s
123+
.section ".note.gnu.property", "a"
124+
.balign 8
125+
.4byte 4
126+
.4byte (ndesc_end - ndesc_begin)
127+
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
128+
.asciz "GNU"
129+
ndesc_begin:
130+
.balign 8
131+
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
132+
.4byte 4
133+
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
134+
.balign 8
135+
ndesc_end:
136+
137+
.text
138+
.globl f2
139+
.type f2,@function
140+
f2:
141+
.globl f3
142+
.type f3, @function
143+
call f3
144+
ret
145+
146+
#--- f3.s
147+
.text
148+
.globl f3
149+
.type f3,@function
150+
f3:
151+
ret
152+
153+
#--- rv32-f3-s.s
154+
.section ".note.gnu.property", "a"
155+
.balign 4
156+
.4byte 4
157+
.4byte (ndesc_end - ndesc_begin)
158+
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
159+
.asciz "GNU"
160+
ndesc_begin:
161+
.balign 4
162+
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
163+
.4byte 4
164+
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
165+
.balign 4
166+
ndesc_end:
167+
168+
.text
169+
.globl f3
170+
.type f3,@function
171+
f3:
172+
ret
173+
174+
#--- f3-s.s
175+
.section ".note.gnu.property", "a"
176+
.balign 8
177+
.4byte 4
178+
.4byte (ndesc_end - ndesc_begin)
179+
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
180+
.asciz "GNU"
181+
ndesc_begin:
182+
.balign 8
183+
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
184+
.4byte 4
185+
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
186+
.balign 8
187+
ndesc_end:
188+
189+
.text
190+
.globl f3
191+
.type f3,@function
192+
f3:
193+
ret

0 commit comments

Comments
 (0)