Skip to content

[LLD][ELF][RISCV][Zicfilp] Handle .note.gnu.property sections for Zicfilp/Zicfiss features #127193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lld/ELF/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ struct Config {
ReportPolicy zGcsReport = ReportPolicy::None;
ReportPolicy zGcsReportDynamic = ReportPolicy::None;
ReportPolicy zExecuteOnlyReport = ReportPolicy::None;
ReportPolicy zZicfilpUnlabeledReport = ReportPolicy::None;
ReportPolicy zZicfilpFuncSigReport = ReportPolicy::None;
ReportPolicy zZicfissReport = ReportPolicy::None;
bool ltoBBAddrMap;
llvm::StringRef ltoBasicBlockSections;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
Expand Down
52 changes: 48 additions & 4 deletions lld/ELF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,18 @@ static void checkOptions(Ctx &ctx) {
<< "--pcrel-optimize is only supported on PowerPC64 targets";
}

if (ctx.arg.relaxGP && ctx.arg.emachine != EM_RISCV)
ErrAlways(ctx) << "--relax-gp is only supported on RISC-V targets";
if (ctx.arg.emachine != EM_RISCV) {
if (ctx.arg.relaxGP)
ErrAlways(ctx) << "--relax-gp is only supported on RISC-V targets";
if (ctx.arg.zZicfilpUnlabeledReport != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfilip-unlabeled-report is only supported on "
"RISC-V targets";
if (ctx.arg.zZicfilpFuncSigReport != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfilip-func-sig-report is only supported on "
"RISC-V targets";
if (ctx.arg.zZicfissReport != ReportPolicy::None)
ErrAlways(ctx) << "-z zicfiss-report is only supported on RISC-V targets";
}

if (ctx.arg.emachine != EM_386 && ctx.arg.emachine != EM_X86_64 &&
ctx.arg.zCetReport != ReportPolicy::None)
Expand Down Expand Up @@ -1637,7 +1647,11 @@ static void readConfigs(Ctx &ctx, opt::InputArgList &args) {
std::make_pair("execute-only-report", &ctx.arg.zExecuteOnlyReport),
std::make_pair("gcs-report", &ctx.arg.zGcsReport),
std::make_pair("gcs-report-dynamic", &ctx.arg.zGcsReportDynamic),
std::make_pair("pauth-report", &ctx.arg.zPauthReport)};
std::make_pair("pauth-report", &ctx.arg.zPauthReport),
std::make_pair("zicfilp-unlabeled-report",
&ctx.arg.zZicfilpUnlabeledReport),
std::make_pair("zicfilp-func-sig-report", &ctx.arg.zZicfilpFuncSigReport),
std::make_pair("zicfiss-report", &ctx.arg.zZicfissReport)};
bool hasGcsReportDynamic = false;
for (opt::Arg *arg : args.filtered(OPT_z)) {
std::pair<StringRef, StringRef> option =
Expand Down Expand Up @@ -2831,9 +2845,12 @@ static void redirectSymbols(Ctx &ctx, ArrayRef<WrappedSymbol> wrapped) {
// For AArch64 PAuth-enabled object files, the core info of all of them must
// match. Missing info for some object files with matching info for remaining
// ones can be allowed (see -z pauth-report).
//
// RISC-V Zicfilp/Zicfiss extension also use the same mechanism to record
// enabled features in the GNU_PROPERTY_RISCV_FEATURE_1_AND bit mask.
static void readSecurityNotes(Ctx &ctx) {
if (ctx.arg.emachine != EM_386 && ctx.arg.emachine != EM_X86_64 &&
ctx.arg.emachine != EM_AARCH64)
ctx.arg.emachine != EM_AARCH64 && ctx.arg.emachine != EM_RISCV)
return;

ctx.arg.andFeatures = -1;
Expand Down Expand Up @@ -2885,6 +2902,33 @@ static void readSecurityNotes(Ctx &ctx) {
<< ": -z cet-report: file does not have "
"GNU_PROPERTY_X86_FEATURE_1_SHSTK property";

if (ctx.arg.emachine == EM_RISCV) {
reportUnless(ctx.arg.zZicfilpUnlabeledReport,
features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED)
<< f
<< ": -z zicfilp-unlabeled-report: file does not have "
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED property";

reportUnless(ctx.arg.zZicfilpFuncSigReport,
features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG)
<< f
<< ": -z zicfilp-func-sig-report: file does not have "
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG property";

if ((features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED) &&
(features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG))
Err(ctx) << f
<< ": file has conflicting properties: "
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED and "
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG";

reportUnless(ctx.arg.zZicfissReport,
features & GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS)
<< f
<< ": -z zicfiss-report: file does not have "
"GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS property";
}

if (ctx.arg.zForceBti && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
if (ctx.arg.zBtiReport == ReportPolicy::None)
Expand Down
28 changes: 20 additions & 8 deletions lld/ELF/InputFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -970,7 +970,7 @@ static void parseGnuPropertyNote(Ctx &ctx, ELFFileBase &f,
}
// Read the following info from the .note.gnu.property section and write it to
// the corresponding fields in `ObjFile`:
// - Feature flags (32 bits) representing x86 or AArch64 features for
// - Feature flags (32 bits) representing x86, AArch64 or RISC-V features for
// hardware-assisted call flow control;
// - AArch64 PAuth ABI core info (16 bytes).
template <class ELFT>
Expand All @@ -979,6 +979,22 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
using Elf_Nhdr = typename ELFT::Nhdr;
using Elf_Note = typename ELFT::Note;

uint32_t featureAndType;
switch (ctx.arg.emachine) {
case EM_386:
case EM_X86_64:
featureAndType = GNU_PROPERTY_X86_FEATURE_1_AND;
break;
case EM_AARCH64:
featureAndType = GNU_PROPERTY_AARCH64_FEATURE_1_AND;
break;
case EM_RISCV:
featureAndType = GNU_PROPERTY_RISCV_FEATURE_1_AND;
break;
default:
return;
}

ArrayRef<uint8_t> data = sec.content();
auto err = [&](const uint8_t *place) -> ELFSyncStream {
auto diag = Err(ctx);
Expand All @@ -999,10 +1015,6 @@ static void readGnuProperty(Ctx &ctx, const InputSection &sec,
continue;
}

uint32_t featureAndType = ctx.arg.emachine == EM_AARCH64
? GNU_PROPERTY_AARCH64_FEATURE_1_AND
: GNU_PROPERTY_X86_FEATURE_1_AND;

// Read a body of a NOTE record, which consists of type-length-value fields.
ArrayRef<uint8_t> desc = note.getDesc(sec.addralign);
const uint8_t *base = sec.content().data();
Expand Down Expand Up @@ -1066,9 +1078,9 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(uint32_t idx,
}

// Object files that use processor features such as Intel Control-Flow
// Enforcement (CET) or AArch64 Branch Target Identification BTI, use a
// .note.gnu.property section containing a bitfield of feature bits like the
// GNU_PROPERTY_X86_FEATURE_1_IBT flag. Read a bitmap containing the flag.
// Enforcement (CET), AArch64 Branch Target Identification BTI or RISC-V
// Zicfilp/Zicfiss extensions, use a .note.gnu.property section containing
// a bitfield of feature bits like the GNU_PROPERTY_X86_FEATURE_1_IBT flag.
//
// Since we merge bitmaps from multiple object files to create a new
// .note.gnu.property containing a single AND'ed bitmap, we discard an input
Expand Down
21 changes: 17 additions & 4 deletions lld/ELF/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,15 +325,28 @@ GnuPropertySection::GnuPropertySection(Ctx &ctx)
ctx.arg.wordsize) {}

void GnuPropertySection::writeTo(uint8_t *buf) {
uint32_t featureAndType;
switch (ctx.arg.emachine) {
case EM_386:
case EM_X86_64:
featureAndType = GNU_PROPERTY_X86_FEATURE_1_AND;
break;
case EM_AARCH64:
featureAndType = GNU_PROPERTY_AARCH64_FEATURE_1_AND;
break;
case EM_RISCV:
featureAndType = GNU_PROPERTY_RISCV_FEATURE_1_AND;
break;
default:
llvm_unreachable(
"target machine does not support .note.gnu.property section");
}

write32(ctx, buf, 4); // Name size
write32(ctx, buf + 4, getSize() - 16); // Content size
write32(ctx, buf + 8, NT_GNU_PROPERTY_TYPE_0); // Type
memcpy(buf + 12, "GNU", 4); // Name string

uint32_t featureAndType = ctx.arg.emachine == EM_AARCH64
? GNU_PROPERTY_AARCH64_FEATURE_1_AND
: GNU_PROPERTY_X86_FEATURE_1_AND;

unsigned offset = 16;
if (ctx.arg.andFeatures != 0) {
write32(ctx, buf + offset + 0, featureAndType); // Feature type
Expand Down
193 changes: 193 additions & 0 deletions lld/test/ELF/riscv-feature-zicfilp-func-sig.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# REQUIRES: riscv
## Test the ZICFILP func-sig feature.
## To lift maintenance burden, most tests are conducted only with 64-bit RISC-V
## Naming convention: *-s.s files enables ZICFILP func-sig.
# RUN: rm -rf %t && split-file %s %t && cd %t
# RUN: llvm-mc --filetype=obj --triple=riscv32 rv32-f1-s.s -o rv32-f1-s.o
# RUN: llvm-mc --filetype=obj --triple=riscv32 rv32-f2-s.s -o rv32-f2-s.o
# RUN: llvm-mc --filetype=obj --triple=riscv32 rv32-f3-s.s -o rv32-f3-s.o

# RUN: llvm-mc --filetype=obj --triple=riscv64 f1-s.s -o f1-s.o
# RUN: llvm-mc --filetype=obj --triple=riscv64 f2.s -o f2.o
# RUN: llvm-mc --filetype=obj --triple=riscv64 f2-s.s -o f2-s.o
# RUN: llvm-mc --filetype=obj --triple=riscv64 f3.s -o f3.o
# RUN: llvm-mc --filetype=obj --triple=riscv64 f3-s.s -o f3-s.o

## ZICFILP-func-sig should be enabled when it's enabled in all inputs
# RUN: ld.lld rv32-f1-s.o rv32-f2-s.o rv32-f3-s.o -o out.rv32 --fatal-warnings
# RUN: llvm-readelf -n out.rv32 | FileCheck --check-prefix=ZICFILP %s
# RUN: ld.lld f1-s.o f2-s.o f3-s.o -o out --fatal-warnings
# RUN: llvm-readelf -n out | FileCheck --check-prefix=ZICFILP %s
# RUN: ld.lld f1-s.o f3-s.o --shared -o out.so --fatal-warnings
# RUN: llvm-readelf -n out.so | FileCheck --check-prefix=ZICFILP %s
# ZICFILP: Properties: RISC-V feature: ZICFILP-func-sig

## ZICFILP-func-sig should not be enabled if it's not enabled in at least one
## input
# RUN: ld.lld f1-s.o f2.o f3-s.o -o out.no --fatal-warnings
# RUN: llvm-readelf -n out.no | count 0
# RUN: ld.lld f2-s.o f3.o --shared -o out.no.so --fatal-warnings
# RUN: llvm-readelf -n out.no.so | count 0

## zicfilp-func-sig-report should report any input files that don't have the
## ZICFILP-func-sig property
# 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
# RUN: not ld.lld f2-s.o f3.o --shared -z zicfilp-func-sig-report=error 2>&1 | FileCheck --check-prefix=REPORT-ERROR %s
# RUN: ld.lld f1-s.o f2-s.o f3-s.o -z zicfilp-func-sig-report=warning 2>&1 | count 0
# REPORT-WARN: warning: f2.o: -z zicfilp-func-sig-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG property
# REPORT-ERROR: error: f3.o: -z zicfilp-func-sig-report: file does not have GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG property

## An invalid -z zicfilp-func-sig-report option should give an error
# RUN: not ld.lld f2-s.o -z zicfilp-func-sig-report=x 2>&1 | FileCheck --check-prefix=INVALID %s
# INVALID: error: unknown -z zicfilp-func-sig-report= value: x

#--- rv32-f1-s.s
.section ".note.gnu.property", "a"
.balign 4
.4byte 4
.4byte (ndesc_end - ndesc_begin)
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
.asciz "GNU"
ndesc_begin:
.balign 4
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
.4byte 4
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
.balign 4
ndesc_end:

.text
.globl _start
.type f1,%function
f1:
call f2
ret

#--- f1-s.s
.section ".note.gnu.property", "a"
.balign 8
.4byte 4
.4byte (ndesc_end - ndesc_begin)
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
.asciz "GNU"
ndesc_begin:
.balign 8
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
.4byte 4
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
.balign 8
ndesc_end:

.text
.globl _start
.type f1,%function
f1:
call f2
ret

#--- f2.s
.text
.globl f2
.type f2,@function
f2:
.globl f3
.type f3, @function
call f3
ret

#--- rv32-f2-s.s
.section ".note.gnu.property", "a"
.balign 4
.4byte 4
.4byte (ndesc_end - ndesc_begin)
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
.asciz "GNU"
ndesc_begin:
.balign 4
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
.4byte 4
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
.balign 4
ndesc_end:

.text
.globl f2
.type f2,@function
f2:
.globl f3
.type f3, @function
call f3
ret

#--- f2-s.s
.section ".note.gnu.property", "a"
.balign 8
.4byte 4
.4byte (ndesc_end - ndesc_begin)
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
.asciz "GNU"
ndesc_begin:
.balign 8
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
.4byte 4
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
.balign 8
ndesc_end:

.text
.globl f2
.type f2,@function
f2:
.globl f3
.type f3, @function
call f3
ret

#--- f3.s
.text
.globl f3
.type f3,@function
f3:
ret

#--- rv32-f3-s.s
.section ".note.gnu.property", "a"
.balign 4
.4byte 4
.4byte (ndesc_end - ndesc_begin)
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
.asciz "GNU"
ndesc_begin:
.balign 4
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
.4byte 4
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
.balign 4
ndesc_end:

.text
.globl f3
.type f3,@function
f3:
ret

#--- f3-s.s
.section ".note.gnu.property", "a"
.balign 8
.4byte 4
.4byte (ndesc_end - ndesc_begin)
.4byte 0x5 // NT_GNU_PROPERTY_TYPE_0
.asciz "GNU"
ndesc_begin:
.balign 8
.4byte 0xc0000000 // GNU_PROPERTY_RISCV_FEATURE_1_AND
.4byte 4
.4byte 4 // GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_FUNC_SIG
.balign 8
ndesc_end:

.text
.globl f3
.type f3,@function
f3:
ret
Loading
Loading