Skip to content

[RISCV][Zicfilp] Emit .note.gnu.property section for Zicfilp CFI unlabeled scheme #141468

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mylai-mtk
Copy link
Contributor

RISC-V Zicfilp-based CFI needs to let the linker/loader know if the binary is built with the mechanism enabled to support proper link-time/load-time management of this feature. The information is encoded as a bit in the .note.gnu.property section. This patch implements emitting the section for RISC-V targets when Zicfilp-based CFI with the "unlabeled" label scheme is enabled.

When Clang receives the -fcf-protection=branch flag, which enables forward-edge CFI protection, it adds the cf-protection-branch attribute to the LLVM module. Additionally, if a branch label scheme is needed, another attribute (cf-branch-label-scheme) is added to indicate it. For RISC-V targets, the cf-protection-branch attribute indicates the adoption of Zicfilp-based forward-edge CFI protection, and the cf-branch-label-scheme attribute selects how the "label" operands of the Zicfilp lpad insns are produced. This patch emits the proper .note.gnu.property feature bit when the RISC-V backend sees that the LLVM module has the cf-protection-branch attribute set to 1, and the cf-branch-label-scheme attribute set to unlabeled, so linkers/loaders know that the object file is built with Zicfilp-based forward-edge CFI protection with the "unlabeled" scheme.

RISC-V Zicfilp-based CFI needs to let the linker/loader know if the binary is
built with the mechanism enabled to support proper link-time/load-time
management of this feature. The information is encoded as a bit in the
`.note.gnu.property` section. This patch implements emitting the section for
RISC-V targets when Zicfilp-based CFI with the "unlabeled" label scheme is
enabled.

When Clang receives the `-fcf-protection=branch` flag, which enables
forward-edge CFI protection, it adds the `cf-protection-branch` attribute to
the LLVM module. Additionally, if a branch label scheme is needed, another
attribute (`cf-branch-label-scheme`) is added to indicate it. For RISC-V
targets, the `cf-protection-branch` attribute indicates the adoption of
Zicfilp-based forward-edge CFI protection, and the `cf-branch-label-scheme`
attribute selects how the "label" operands of the Zicfilp `lpad` insns are
produced. This patch emits the proper `.note.gnu.property` feature bit when
the RISC-V backend sees that the LLVM module has the `cf-protection-branch`
attribute set to `1`, and the `cf-branch-label-scheme` attribute set to
`unlabeled`, so linkers/loaders know that the object file is built with
Zicfilp-based forward-edge CFI protection with the "unlabeled" scheme.
@llvmbot
Copy link
Member

llvmbot commented May 26, 2025

@llvm/pr-subscribers-llvm-support

@llvm/pr-subscribers-backend-risc-v

Author: Ming-Yi Lai (mylai-mtk)

Changes

RISC-V Zicfilp-based CFI needs to let the linker/loader know if the binary is built with the mechanism enabled to support proper link-time/load-time management of this feature. The information is encoded as a bit in the .note.gnu.property section. This patch implements emitting the section for RISC-V targets when Zicfilp-based CFI with the "unlabeled" label scheme is enabled.

When Clang receives the -fcf-protection=branch flag, which enables forward-edge CFI protection, it adds the cf-protection-branch attribute to the LLVM module. Additionally, if a branch label scheme is needed, another attribute (cf-branch-label-scheme) is added to indicate it. For RISC-V targets, the cf-protection-branch attribute indicates the adoption of Zicfilp-based forward-edge CFI protection, and the cf-branch-label-scheme attribute selects how the "label" operands of the Zicfilp lpad insns are produced. This patch emits the proper .note.gnu.property feature bit when the RISC-V backend sees that the LLVM module has the cf-protection-branch attribute set to 1, and the cf-branch-label-scheme attribute set to unlabeled, so linkers/loaders know that the object file is built with Zicfilp-based forward-edge CFI protection with the "unlabeled" scheme.


Full diff: https://github.com/llvm/llvm-project/pull/141468.diff

3 Files Affected:

  • (modified) llvm/include/llvm/Support/RISCVISAUtils.h (+19)
  • (modified) llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp (+32-3)
  • (added) llvm/test/CodeGen/RISCV/note-gnu-property-zicfilp-unlabeled.ll (+27)
diff --git a/llvm/include/llvm/Support/RISCVISAUtils.h b/llvm/include/llvm/Support/RISCVISAUtils.h
index 165bb08d66431..e2dd833de74fd 100644
--- a/llvm/include/llvm/Support/RISCVISAUtils.h
+++ b/llvm/include/llvm/Support/RISCVISAUtils.h
@@ -14,7 +14,9 @@
 #define LLVM_SUPPORT_RISCVISAUTILS_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/Support/Compiler.h"
+#include <cassert>
 #include <map>
 #include <string>
 
@@ -43,6 +45,23 @@ struct ExtensionComparator {
 typedef std::map<std::string, ExtensionVersion, ExtensionComparator>
     OrderedExtensionMap;
 
+enum class ZicfilpLabelSchemeKind {
+  Invalid,
+  Unlabeled,
+  FuncSig,
+};
+
+// See clang::getCFBranchLabelSchemeFlagVal() for possible CFBranchLabelScheme
+inline ZicfilpLabelSchemeKind
+getZicfilpLabelScheme(const StringRef CFBranchLabelScheme) {
+  const auto Ret = StringSwitch<ZicfilpLabelSchemeKind>(CFBranchLabelScheme)
+                       .Case("unlabeled", ZicfilpLabelSchemeKind::Unlabeled)
+                       .Case("func-sig", ZicfilpLabelSchemeKind::FuncSig)
+                       .Default(ZicfilpLabelSchemeKind::Invalid);
+  assert(Ret != ZicfilpLabelSchemeKind::Invalid);
+  return Ret;
+}
+
 } // namespace RISCVISAUtils
 
 } // namespace llvm
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index 72f1596d79a02..62d5888e77daa 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -946,12 +946,41 @@ void RISCVAsmPrinter::EmitHwasanMemaccessSymbols(Module &M) {
 }
 
 void RISCVAsmPrinter::emitNoteGnuProperty(const Module &M) {
+  uint32_t Feature1And = 0;
   if (const Metadata *const Flag = M.getModuleFlag("cf-protection-return");
+      Flag && !mdconst::extract<ConstantInt>(Flag)->isZero())
+    Feature1And |= ELF::GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS;
+
+  if (const Metadata *const Flag = M.getModuleFlag("cf-protection-branch");
       Flag && !mdconst::extract<ConstantInt>(Flag)->isZero()) {
-    RISCVTargetStreamer &RTS =
-        static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer());
-    RTS.emitNoteGnuPropertySection(ELF::GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS);
+    using namespace llvm::RISCVISAUtils;
+    const Metadata *const CFBranchLabelSchemeFlag =
+        M.getModuleFlag("cf-branch-label-scheme");
+    assert(CFBranchLabelSchemeFlag &&
+           "cf-protection=branch should come with cf-branch-label-scheme=... "
+           "on RISC-V targets");
+    const StringRef CFBranchLabelScheme =
+        cast<MDString>(CFBranchLabelSchemeFlag)->getString();
+    switch (getZicfilpLabelScheme(CFBranchLabelScheme)) {
+    case ZicfilpLabelSchemeKind::Invalid:
+      llvm_unreachable("invalid RISC-V Zicfilp label scheme");
+    case ZicfilpLabelSchemeKind::Unlabeled:
+      Feature1And |= ELF::GNU_PROPERTY_RISCV_FEATURE_1_CFI_LP_UNLABELED;
+      break;
+    case ZicfilpLabelSchemeKind::FuncSig:
+      // TODO: Emit the func-sig bit after the feature is implemented
+      report_fatal_error("The complete func-sig label scheme feature had not "
+                         "been implemented yet");
+      break;
+    }
   }
+
+  if (!Feature1And)
+    return;
+
+  RISCVTargetStreamer &RTS =
+      static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer());
+  RTS.emitNoteGnuPropertySection(Feature1And);
 }
 
 static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
diff --git a/llvm/test/CodeGen/RISCV/note-gnu-property-zicfilp-unlabeled.ll b/llvm/test/CodeGen/RISCV/note-gnu-property-zicfilp-unlabeled.ll
new file mode 100644
index 0000000000000..cfc9ca44c1eb7
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/note-gnu-property-zicfilp-unlabeled.ll
@@ -0,0 +1,27 @@
+; RUN: llc --mtriple=riscv32 --filetype=obj -o - %s | llvm-readelf -n - | FileCheck --check-prefixes=READELF %s
+; RUN: llc --mtriple=riscv64 --filetype=obj -o - %s | llvm-readelf -n - | FileCheck --check-prefixes=READELF %s
+; RUN: llc --mtriple=riscv32 -o - %s | FileCheck --check-prefixes=ASM,ASM32 %s
+; RUN: llc --mtriple=riscv64 -o - %s | FileCheck --check-prefixes=ASM,ASM64 %s
+
+; READELF: Properties: RISC-V feature: ZICFILP-unlabeled
+
+; ASM:                .section        ".note.GNU-stack","",@progbits
+; ASM-NEXT:           .section        .note.gnu.property,"a",@note
+; ASM-NEXT:           .word   4
+; ASM-NEXT:           .word   .Ltmp1-.Ltmp0
+; ASM-NEXT:           .word   5
+; ASM-NEXT:           .asciz  "GNU"
+; ASM-NEXT:   .Ltmp0:
+; ASM32-NEXT:         .p2align        2, 0x0
+; ASM64-NEXT:         .p2align        3, 0x0
+; ASM-NEXT:           .word   3221225472
+; ASM-NEXT:           .word   4
+; ASM-NEXT:           .word   1
+; ASM32-NEXT:         .p2align        2, 0x0
+; ASM64-NEXT:         .p2align        3, 0x0
+; ASM-NEXT:   .Ltmp1:
+
+!llvm.module.flags = !{!0, !1}
+
+!0 = !{i32 8, !"cf-protection-branch", i32 1}
+!1 = !{i32 1, !"cf-branch-label-scheme", !"unlabeled"}

@mylai-mtk mylai-mtk changed the title [RISCV] Emit .note.gnu.property section for Zicfilp CFI unlabeled scheme [RISCV][Zicfilp] Emit .note.gnu.property section for Zicfilp CFI unlabeled scheme May 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants