[llvm] [SFrames] Add FDEs for functions with .cfi_startproc (PR #154213)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 22 09:54:19 PDT 2025
https://github.com/Sterling-Augustine updated https://github.com/llvm/llvm-project/pull/154213
>From 6ce089d78dab3c49dc4c6d4168917f1c54b43331 Mon Sep 17 00:00:00 2001
From: Sterling Augustine <saugustine at google.com>
Date: Tue, 12 Aug 2025 16:52:09 -0700
Subject: [PATCH 1/3] Generate skeleton SFrame Function Description Entries.
---
llvm/lib/MC/MCSFrame.cpp | 84 +++++++++++++++++++++++++++++++++--
llvm/test/MC/ELF/cfi-sframe.s | 12 +++--
2 files changed, 90 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/MC/MCSFrame.cpp b/llvm/lib/MC/MCSFrame.cpp
index 447f22eaaf17e..043f2adf91911 100644
--- a/llvm/lib/MC/MCSFrame.cpp
+++ b/llvm/lib/MC/MCSFrame.cpp
@@ -9,6 +9,7 @@
#include "llvm/MC/MCSFrame.h"
#include "llvm/BinaryFormat/SFrame.h"
#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCObjectStreamer.h"
#include "llvm/MC/MCSection.h"
@@ -20,12 +21,62 @@ using namespace sframe;
namespace {
+// High-level structure to track info needed to emit a sframe_func_desc_entry
+// and its associated FREs.
+struct SFrameFDE {
+ // Reference to the original dwarf frame to avoid copying.
+ const MCDwarfFrameInfo &DFrame;
+ // Label where this FDE's FREs start.
+ MCSymbol *FREStart;
+
+ SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES)
+ : DFrame(DF), FREStart(FRES) {}
+
+ void emit(MCObjectStreamer &S, const MCSymbol* FRESubSectionStart) {
+ MCContext &C = S.getContext();
+
+ // sfde_func_start_address
+ const MCExpr *V = C.getAsmInfo()->getExprForFDESymbol(
+ &(*DFrame.Begin), C.getObjectFileInfo()->getFDEEncoding(), S);
+ S.emitValue(V, sizeof(int32_t));
+
+ // sfde_func_size
+ S.emitAbsoluteSymbolDiff(DFrame.End, DFrame.Begin, sizeof(uint32_t));
+
+ // sfde_func_start_fre_off
+ auto *F = S.getCurrentFragment();
+ const MCExpr *Diff =
+ MCBinaryExpr::createSub(MCSymbolRefExpr::create(FREStart, C),
+ MCSymbolRefExpr::create(FRESubSectionStart, C), C);
+
+ F->addFixup(MCFixup::create(F->getContents().size(), Diff,
+ MCFixup::getDataKindForSize(4)));
+ S.emitInt32(0);
+
+ // sfde_func_start_num_fres
+ S.emitInt32(0);
+
+ // sfde_func_info word
+ FDEInfo I;
+ I.setFuncInfo(0 /* No pauth key */, FDEType::PCInc, FREType::Addr1);
+ S.emitInt8(I.Info);
+
+ // sfde_func_rep_size. Not relevant in non-PCMASK fdes.
+ S.emitInt8(0);
+
+ // sfde_func_padding2
+ S.emitInt16(0);
+ }
+};
+
// Emitting these field-by-field, instead of constructing the actual structures
// lets Streamer do target endian-fixups for free.
class SFrameEmitterImpl {
MCObjectStreamer &Streamer;
+ SmallVector<SFrameFDE> FDEs;
ABI SFrameABI;
+
MCSymbol *FDESubSectionStart;
MCSymbol *FRESubSectionStart;
MCSymbol *FRESubSectionEnd;
@@ -36,16 +87,22 @@ class SFrameEmitterImpl {
.getObjectFileInfo()
->getSFrameABIArch()
.has_value());
+ FDEs.reserve(Streamer.getDwarfFrameInfos().size());
SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch();
+
FDESubSectionStart = Streamer.getContext().createTempSymbol();
FRESubSectionStart = Streamer.getContext().createTempSymbol();
FRESubSectionEnd = Streamer.getContext().createTempSymbol();
}
+ void BuildSFDE(const MCDwarfFrameInfo &DF) {
+ FDEs.emplace_back(DF, Streamer.getContext().createTempSymbol());
+ }
+
void emitPreamble() {
Streamer.emitInt16(Magic);
Streamer.emitInt8(static_cast<uint8_t>(Version::V2));
- Streamer.emitInt8(0);
+ Streamer.emitInt8(static_cast<uint8_t>(Flags::FDEFuncStartPCRel));
}
void emitHeader() {
@@ -59,9 +116,11 @@ class SFrameEmitterImpl {
// sfh_auxhdr_len
Streamer.emitInt8(0);
// shf_num_fdes
- Streamer.emitInt32(0);
+ Streamer.emitInt32(FDEs.size());
+
// shf_num_fres
Streamer.emitInt32(0);
+
// shf_fre_len
Streamer.emitAbsoluteSymbolDiff(FRESubSectionEnd, FRESubSectionStart,
sizeof(int32_t));
@@ -72,10 +131,17 @@ class SFrameEmitterImpl {
sizeof(uint32_t));
}
- void emitFDEs() { Streamer.emitLabel(FDESubSectionStart); }
+ void emitFDEs() {
+ Streamer.emitLabel(FDESubSectionStart);
+ for (auto &FDE : FDEs) {
+ FDE.emit(Streamer, FRESubSectionStart);
+ }
+ }
void emitFREs() {
Streamer.emitLabel(FRESubSectionStart);
+ for (auto FDE : FDEs)
+ Streamer.emitLabel(FDE.FREStart);
Streamer.emitLabel(FRESubSectionEnd);
}
};
@@ -84,7 +150,19 @@ class SFrameEmitterImpl {
void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
MCContext &Context = Streamer.getContext();
+ // If this target doesn't support sframes, return now. Gas doesn't warn in
+ // this case, but if we want to, it should be done at option-parsing time,
+ // rather than here.
+ if (!Streamer.getContext().getObjectFileInfo()->getSFrameABIArch().has_value())
+ return;
+
SFrameEmitterImpl Emitter(Streamer);
+ ArrayRef<MCDwarfFrameInfo> FrameArray = Streamer.getDwarfFrameInfos();
+
+ // Both the header itself and the FDEs include various offsets and counts.
+ // Therefore, all of this must be precomputed.
+ for (const auto& DFrame : FrameArray)
+ Emitter.BuildSFDE(DFrame);
MCSection *Section = Context.getObjectFileInfo()->getSFrameSection();
// Not strictly necessary, but gas always aligns to 8, so match that.
diff --git a/llvm/test/MC/ELF/cfi-sframe.s b/llvm/test/MC/ELF/cfi-sframe.s
index 45fce241c8764..f6e3dfbd4548d 100644
--- a/llvm/test/MC/ELF/cfi-sframe.s
+++ b/llvm/test/MC/ELF/cfi-sframe.s
@@ -10,17 +10,23 @@ f1:
nop
.cfi_endproc
+f2:
+ .cfi_startproc
+ nop
+ nop
+ .cfi_endproc
+
// CHECK: SFrame section '.sframe' {
// CHECK-NEXT: Header {
// CHECK-NEXT: Magic: 0xDEE2
// CHECK-NEXT: Version: V2 (0x2)
-// CHECK-NEXT: Flags [ (0x0)
+// CHECK-NEXT: Flags [ (0x4)
// CHECK: ABI: AMD64EndianLittle (0x3)
// CHECK-NEXT: CFA fixed FP offset (unused): 0
// CHECK-NEXT: CFA fixed RA offset: 0
// CHECK-NEXT: Auxiliary header length: 0
-// CHECK-NEXT: Num FDEs: 0
+// CHECK-NEXT: Num FDEs: 2
// CHECK-NEXT: Num FREs: 0
// CHECK-NEXT: FRE subsection length: 0
// CHECK-NEXT: FDE subsection offset: 0
-// CHECK-NEXT: FRE subsection offset: 0
+// CHECK-NEXT: FRE subsection offset: 40
>From 67b92059f3d01a3e84478a335a177cf935b4c016 Mon Sep 17 00:00:00 2001
From: Sterling Augustine <saugustine at google.com>
Date: Mon, 18 Aug 2025 15:18:50 -0700
Subject: [PATCH 2/3] Fix formatting
---
llvm/lib/MC/MCSFrame.cpp | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/MC/MCSFrame.cpp b/llvm/lib/MC/MCSFrame.cpp
index 043f2adf91911..45d407eecf04b 100644
--- a/llvm/lib/MC/MCSFrame.cpp
+++ b/llvm/lib/MC/MCSFrame.cpp
@@ -8,8 +8,8 @@
#include "llvm/MC/MCSFrame.h"
#include "llvm/BinaryFormat/SFrame.h"
-#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCObjectStreamer.h"
#include "llvm/MC/MCSection.h"
@@ -32,7 +32,7 @@ struct SFrameFDE {
SFrameFDE(const MCDwarfFrameInfo &DF, MCSymbol *FRES)
: DFrame(DF), FREStart(FRES) {}
- void emit(MCObjectStreamer &S, const MCSymbol* FRESubSectionStart) {
+ void emit(MCObjectStreamer &S, const MCSymbol *FRESubSectionStart) {
MCContext &C = S.getContext();
// sfde_func_start_address
@@ -45,9 +45,9 @@ struct SFrameFDE {
// sfde_func_start_fre_off
auto *F = S.getCurrentFragment();
- const MCExpr *Diff =
- MCBinaryExpr::createSub(MCSymbolRefExpr::create(FREStart, C),
- MCSymbolRefExpr::create(FRESubSectionStart, C), C);
+ const MCExpr *Diff = MCBinaryExpr::createSub(
+ MCSymbolRefExpr::create(FREStart, C),
+ MCSymbolRefExpr::create(FRESubSectionStart, C), C);
F->addFixup(MCFixup::create(F->getContents().size(), Diff,
MCFixup::getDataKindForSize(4)));
@@ -57,7 +57,7 @@ struct SFrameFDE {
S.emitInt32(0);
// sfde_func_info word
- FDEInfo I;
+ FDEInfo<endianness::native> I;
I.setFuncInfo(0 /* No pauth key */, FDEType::PCInc, FREType::Addr1);
S.emitInt8(I.Info);
@@ -76,7 +76,6 @@ class SFrameEmitterImpl {
MCObjectStreamer &Streamer;
SmallVector<SFrameFDE> FDEs;
ABI SFrameABI;
-
MCSymbol *FDESubSectionStart;
MCSymbol *FRESubSectionStart;
MCSymbol *FRESubSectionEnd;
@@ -89,7 +88,6 @@ class SFrameEmitterImpl {
.has_value());
FDEs.reserve(Streamer.getDwarfFrameInfos().size());
SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch();
-
FDESubSectionStart = Streamer.getContext().createTempSymbol();
FRESubSectionStart = Streamer.getContext().createTempSymbol();
FRESubSectionEnd = Streamer.getContext().createTempSymbol();
@@ -117,10 +115,8 @@ class SFrameEmitterImpl {
Streamer.emitInt8(0);
// shf_num_fdes
Streamer.emitInt32(FDEs.size());
-
// shf_num_fres
Streamer.emitInt32(0);
-
// shf_fre_len
Streamer.emitAbsoluteSymbolDiff(FRESubSectionEnd, FRESubSectionStart,
sizeof(int32_t));
@@ -153,7 +149,10 @@ void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
// If this target doesn't support sframes, return now. Gas doesn't warn in
// this case, but if we want to, it should be done at option-parsing time,
// rather than here.
- if (!Streamer.getContext().getObjectFileInfo()->getSFrameABIArch().has_value())
+ if (!Streamer.getContext()
+ .getObjectFileInfo()
+ ->getSFrameABIArch()
+ .has_value())
return;
SFrameEmitterImpl Emitter(Streamer);
@@ -161,7 +160,7 @@ void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
// Both the header itself and the FDEs include various offsets and counts.
// Therefore, all of this must be precomputed.
- for (const auto& DFrame : FrameArray)
+ for (const auto &DFrame : FrameArray)
Emitter.BuildSFDE(DFrame);
MCSection *Section = Context.getObjectFileInfo()->getSFrameSection();
>From 80251ed056fd465839cfbdf96a9dce4b1edbd80d Mon Sep 17 00:00:00 2001
From: Sterling Augustine <saugustine at google.com>
Date: Thu, 21 Aug 2025 16:15:38 -0700
Subject: [PATCH 3/3] Address comments
---
llvm/lib/MC/MCSFrame.cpp | 2 +-
llvm/test/MC/ELF/cfi-sframe.s | 42 ++++++++++++++++++++++++++++++++++-
2 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/MC/MCSFrame.cpp b/llvm/lib/MC/MCSFrame.cpp
index 45d407eecf04b..ee17b774e4740 100644
--- a/llvm/lib/MC/MCSFrame.cpp
+++ b/llvm/lib/MC/MCSFrame.cpp
@@ -136,7 +136,7 @@ class SFrameEmitterImpl {
void emitFREs() {
Streamer.emitLabel(FRESubSectionStart);
- for (auto FDE : FDEs)
+ for (auto &FDE : FDEs)
Streamer.emitLabel(FDE.FREStart);
Streamer.emitLabel(FRESubSectionEnd);
}
diff --git a/llvm/test/MC/ELF/cfi-sframe.s b/llvm/test/MC/ELF/cfi-sframe.s
index f6e3dfbd4548d..66b9ee61bdaff 100644
--- a/llvm/test/MC/ELF/cfi-sframe.s
+++ b/llvm/test/MC/ELF/cfi-sframe.s
@@ -4,7 +4,6 @@
// RUN: llvm-readelf --sframe %t.o | FileCheck %s
.cfi_sections .sframe
-
f1:
.cfi_startproc
nop
@@ -30,3 +29,44 @@ f2:
// CHECK-NEXT: FRE subsection length: 0
// CHECK-NEXT: FDE subsection offset: 0
// CHECK-NEXT: FRE subsection offset: 40
+// CHECK: Function Index [
+// CHECK-NEXT: FuncDescEntry [0] {
+// CHECK-NEXT: PC {
+// CHECK-NEXT: Relocation: {{.*}}32{{.*}}
+// CHECK-NEXT: Symbol Name: .text
+// CHECK-NEXT: Start Address: 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: Size: 0x1
+// CHECK-NEXT: Start FRE Offset: 0x0
+// CHECK-NEXT: Num FREs: 0
+// CHECK-NEXT: Info {
+// CHECK-NEXT: FRE Type: Addr1 (0x0)
+// CHECK-NEXT: FDE Type: PCInc (0x0)
+// CHECK-NEXT: Raw: 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: Repetitive block size (unused): 0x0
+// CHECK-NEXT: Padding2: 0x0
+// CHECK-NEXT: FREs [
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: FuncDescEntry [1] {
+// CHECK-NEXT: PC {
+// CHECK-NEXT: Relocation: R_X86_64_PC32
+// CHECK-NEXT: Symbol Name: .text
+// CHECK-NEXT: Start Address: {{.*}}
+// CHECK-NEXT: }
+// CHECK-NEXT: Size: 0x2
+// CHECK-NEXT: Start FRE Offset: 0x0
+// CHECK-NEXT: Num FREs: 0
+// CHECK-NEXT: Info {
+// CHECK-NEXT: FRE Type: Addr1 (0x0)
+// CHECK-NEXT: FDE Type: PCInc (0x0)
+// CHECK-NEXT: Raw: 0x0
+// CHECK-NEXT: }
+// CHECK-NEXT: Repetitive block size (unused): 0x0
+// CHECK-NEXT: Padding2: 0x0
+// CHECK-NEXT: FREs [
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
More information about the llvm-commits
mailing list