[RISCV] Emit .note.gnu.property section when Zicfiss-based shadow stack is enabled (#127036)
RISC-V Zicfiss-based shadow stack 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 Zicfiss-based
shadow stack is enabled.
When Clang receives the `-fcf-protection=return` flag, it adds the
`hw-shadow-stack` attribute to LLVM functions, and adds a non-zero
valued attribute named `cf-protection-return` to the LLVM module it
generates. The backend depends on the `hw-shadow-stack` attributes to
generate Zicfiss-based shadow stack instructions for each function, but
at the module scope, the `cf-protection-return` attribute is a better
indication of whether the translation unit is built with Zicfiss-based
shadow stack enabled, so this patch emits the `.note.gnu.property`
section with the "Zicfiss-based shadow stack" bit toggled on when it
sees the `cf-protection-return` attribute.
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
index 5785f7d..6df8b18 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
@@ -13,8 +13,15 @@
#include "RISCVTargetStreamer.h"
#include "RISCVBaseInfo.h"
#include "RISCVMCTargetDesc.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCSectionELF.h"
+#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
+#include "llvm/Support/Alignment.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/RISCVAttributes.h"
#include "llvm/TargetParser/RISCVISAInfo.h"
@@ -53,6 +60,55 @@
void RISCVTargetStreamer::emitIntTextAttribute(unsigned Attribute,
unsigned IntValue,
StringRef StringValue) {}
+
+void RISCVTargetStreamer::emitNoteGnuPropertySection(
+ const uint32_t Feature1And) {
+ MCStreamer &OutStreamer = getStreamer();
+ MCContext &Ctx = OutStreamer.getContext();
+
+ const Triple &Triple = Ctx.getTargetTriple();
+ Align NoteAlign;
+ if (Triple.isArch64Bit()) {
+ NoteAlign = Align(8);
+ } else {
+ assert(Triple.isArch32Bit());
+ NoteAlign = Align(4);
+ }
+
+ assert(Ctx.getObjectFileType() == MCContext::Environment::IsELF);
+ MCSection *const NoteSection =
+ Ctx.getELFSection(".note.gnu.property", ELF::SHT_NOTE, ELF::SHF_ALLOC);
+ NoteSection->setAlignment(NoteAlign);
+ OutStreamer.pushSection();
+ OutStreamer.switchSection(NoteSection);
+
+ // Emit the note header
+ OutStreamer.emitIntValue(4, 4); // n_namsz
+
+ MCSymbol *const NDescBeginSym = Ctx.createTempSymbol();
+ MCSymbol *const NDescEndSym = Ctx.createTempSymbol();
+ const MCExpr *const NDescSzExpr =
+ MCBinaryExpr::createSub(MCSymbolRefExpr::create(NDescEndSym, Ctx),
+ MCSymbolRefExpr::create(NDescBeginSym, Ctx), Ctx);
+
+ OutStreamer.emitValue(NDescSzExpr, 4); // n_descsz
+ OutStreamer.emitIntValue(ELF::NT_GNU_PROPERTY_TYPE_0, 4); // n_type
+ OutStreamer.emitBytes(StringRef("GNU", 4)); // n_name
+
+ // Emit n_desc field
+ OutStreamer.emitLabel(NDescBeginSym);
+ OutStreamer.emitValueToAlignment(NoteAlign);
+
+ // Emit the feature_1_and property
+ OutStreamer.emitIntValue(ELF::GNU_PROPERTY_RISCV_FEATURE_1_AND, 4); // pr_type
+ OutStreamer.emitIntValue(4, 4); // pr_datasz
+ OutStreamer.emitIntValue(Feature1And, 4); // pr_data
+ OutStreamer.emitValueToAlignment(NoteAlign); // pr_padding
+
+ OutStreamer.emitLabel(NDescEndSym);
+ OutStreamer.popSection();
+}
+
void RISCVTargetStreamer::setTargetABI(RISCVABI::ABI ABI) {
assert(ABI != RISCVABI::ABI_Unknown && "Improperly initialized target ABI");
TargetABI = ABI;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
index 169cb0f..d7b3b1e 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
@@ -58,6 +58,7 @@
virtual void emitTextAttribute(unsigned Attribute, StringRef String);
virtual void emitIntTextAttribute(unsigned Attribute, unsigned IntValue,
StringRef StringValue);
+ void emitNoteGnuPropertySection(const uint32_t Feature1And);
void emitTargetAttributes(const MCSubtargetInfo &STI, bool EmitStackAlign);
void setTargetABI(RISCVABI::ABI ABI);
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index e40bd18..5aa323a 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -112,6 +112,8 @@
void emitFunctionEntryLabel() override;
bool emitDirectiveOptionArch();
+ void emitNoteGnuProperty(const Module &M);
+
private:
void emitAttributes(const MCSubtargetInfo &SubtargetInfo);
@@ -581,8 +583,10 @@
RISCVTargetStreamer &RTS =
static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer());
- if (TM.getTargetTriple().isOSBinFormatELF())
+ if (TM.getTargetTriple().isOSBinFormatELF()) {
RTS.finishAttributeSection();
+ emitNoteGnuProperty(M);
+ }
EmitHwasanMemaccessSymbols(M);
}
@@ -941,6 +945,15 @@
}
}
+void RISCVAsmPrinter::emitNoteGnuProperty(const Module &M) {
+ if (const Metadata *const Flag = M.getModuleFlag("cf-protection-return");
+ Flag && !mdconst::extract<ConstantInt>(Flag)->isZero()) {
+ RISCVTargetStreamer &RTS =
+ static_cast<RISCVTargetStreamer &>(*OutStreamer->getTargetStreamer());
+ RTS.emitNoteGnuPropertySection(ELF::GNU_PROPERTY_RISCV_FEATURE_1_CFI_SS);
+ }
+}
+
static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
const AsmPrinter &AP) {
MCContext &Ctx = AP.OutContext;
diff --git a/llvm/test/CodeGen/RISCV/note-gnu-property-zicfiss.ll b/llvm/test/CodeGen/RISCV/note-gnu-property-zicfiss.ll
new file mode 100644
index 0000000..24d63cb
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/note-gnu-property-zicfiss.ll
@@ -0,0 +1,31 @@
+; 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: ZICFISS
+
+; 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 2
+; ASM32-NEXT: .p2align 2, 0x0
+; ASM64-NEXT: .p2align 3, 0x0
+; ASM-NEXT: .Ltmp1:
+
+define i32 @f() "hw-shadow-stack" {
+entry:
+ ret i32 0
+}
+
+!llvm.module.flags = !{!0}
+
+!0 = !{i32 8, !"cf-protection-return", i32 1}