[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