[llvm] 2e9944a - Generate an .sframe section with a skeleton header (#151223)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 12 12:57:34 PDT 2025
Author: Sterling-Augustine
Date: 2025-08-12T12:57:31-07:00
New Revision: 2e9944a03e6bdda64f266aa4b9fe88d81fdf16cd
URL: https://github.com/llvm/llvm-project/commit/2e9944a03e6bdda64f266aa4b9fe88d81fdf16cd
DIFF: https://github.com/llvm/llvm-project/commit/2e9944a03e6bdda64f266aa4b9fe88d81fdf16cd.diff
LOG: Generate an .sframe section with a skeleton header (#151223)
This continues the sframe implementation discussed previously.
Of note, this also adds some target dependent functions to the object
file. Additional fields will be needed later. It would be possible to do
all of this inside the sframe implementation itself if it feels a little
messy and specialized, but generally I think that target info goes with
target info.
Another question is if we want a sentinel value for unimplemented sframe
abi arches, or a std::optional. Both work.
Added:
llvm/include/llvm/MC/MCSFrame.h
llvm/lib/MC/MCSFrame.cpp
llvm/test/MC/ELF/cfi-sframe.s
Modified:
llvm/include/llvm/MC/MCObjectFileInfo.h
llvm/lib/MC/CMakeLists.txt
llvm/lib/MC/MCObjectFileInfo.cpp
llvm/lib/MC/MCObjectStreamer.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h
index d69560ca2d652..ed7f462c3c598 100644
--- a/llvm/include/llvm/MC/MCObjectFileInfo.h
+++ b/llvm/include/llvm/MC/MCObjectFileInfo.h
@@ -13,6 +13,7 @@
#ifndef LLVM_MC_MCOBJECTFILEINFO_H
#define LLVM_MC_MCOBJECTFILEINFO_H
+#include "llvm/BinaryFormat/SFrame.h"
#include "llvm/BinaryFormat/Swift.h"
#include "llvm/MC/MCSection.h"
#include "llvm/Support/Compiler.h"
@@ -50,6 +51,9 @@ class LLVM_ABI MCObjectFileInfo {
/// Compact unwind encoding indicating that we should emit only an EH frame.
unsigned CompactUnwindDwarfEHFrameOnly = 0;
+ /// SFrame ABI architecture byte.
+ std::optional<sframe::ABI> SFrameABIArch = {};
+
/// Section directive for standard text.
MCSection *TextSection = nullptr;
@@ -177,6 +181,9 @@ class LLVM_ABI MCObjectFileInfo {
/// It is initialized on demand so it can be overwritten (with uniquing).
MCSection *EHFrameSection = nullptr;
+ /// SFrame section.
+ MCSection *SFrameSection = nullptr;
+
/// Section containing metadata on function stack sizes.
MCSection *StackSizesSection = nullptr;
@@ -269,6 +276,7 @@ class LLVM_ABI MCObjectFileInfo {
return CompactUnwindDwarfEHFrameOnly;
}
+ std::optional<sframe::ABI> getSFrameABIArch() const { return SFrameABIArch; }
virtual unsigned getTextSectionAlignment() const { return 4; }
MCSection *getTextSection() const { return TextSection; }
MCSection *getDataSection() const { return DataSection; }
@@ -450,6 +458,7 @@ class LLVM_ABI MCObjectFileInfo {
MCSection *getTOCBaseSection() const { return TOCBaseSection; }
MCSection *getEHFrameSection() const { return EHFrameSection; }
+ MCSection *getSFrameSection() const { return SFrameSection; }
bool isPositionIndependent() const { return PositionIndependent; }
diff --git a/llvm/include/llvm/MC/MCSFrame.h b/llvm/include/llvm/MC/MCSFrame.h
new file mode 100644
index 0000000000000..8f182a86d1ab1
--- /dev/null
+++ b/llvm/include/llvm/MC/MCSFrame.h
@@ -0,0 +1,32 @@
+//===- MCSFrame.h - Machine Code SFrame support ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the declaration of MCSFrameEmitter to support emitting
+// sframe unwinding info from .cfi_* directives. It relies on FDEs and CIEs
+// created for Dwarf frame info, but emits that info in a
diff erent format.
+//
+// See https://sourceware.org/binutils/docs-2.41/sframe-spec.html
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_MC_MCSFRAME_H
+#define LLVM_MC_MCSFRAME_H
+
+namespace llvm {
+
+class MCObjectStreamer;
+
+class MCSFrameEmitter {
+public:
+ // Emit the sframe section.
+ //
+ // \param Streamer - Emit into this stream.
+ static void emit(MCObjectStreamer &Streamer);
+};
+
+} // end namespace llvm
+#endif // LLVM_MC_MCSFRAME_H
diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt
index 18a85b3fed08c..1e1d0a6d00601 100644
--- a/llvm/lib/MC/CMakeLists.txt
+++ b/llvm/lib/MC/CMakeLists.txt
@@ -45,6 +45,7 @@ add_llvm_component_library(LLVMMC
MCSection.cpp
MCSectionMachO.cpp
MCStreamer.cpp
+ MCSFrame.cpp
MCSPIRVStreamer.cpp
MCSubtargetInfo.cpp
MCSymbol.cpp
diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp
index 4ac73ab34aeda..d274c88b1992f 100644
--- a/llvm/lib/MC/MCObjectFileInfo.cpp
+++ b/llvm/lib/MC/MCObjectFileInfo.cpp
@@ -10,6 +10,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/BinaryFormat/SFrame.h"
#include "llvm/BinaryFormat/Wasm.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
@@ -380,6 +381,19 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) {
unsigned EHSectionType = T.getArch() == Triple::x86_64
? ELF::SHT_X86_64_UNWIND
: ELF::SHT_PROGBITS;
+ switch (T.getArch()) {
+ case Triple::x86_64:
+ SFrameABIArch = sframe::ABI::AMD64EndianLittle;
+ break;
+ case Triple::aarch64:
+ SFrameABIArch = sframe::ABI::AArch64EndianLittle;
+ break;
+ case Triple::aarch64_be:
+ SFrameABIArch = sframe::ABI::AArch64EndianBig;
+ break;
+ default:
+ break;
+ }
// Solaris requires
diff erent flags for .eh_frame to seemingly every other
// platform.
@@ -537,6 +551,9 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) {
EHFrameSection =
Ctx->getELFSection(".eh_frame", EHSectionType, EHSectionFlags);
+ SFrameSection =
+ Ctx->getELFSection(".sframe", ELF::SHT_GNU_SFRAME, ELF::SHF_ALLOC);
+
CallGraphSection = Ctx->getELFSection(".callgraph", ELF::SHT_PROGBITS, 0);
StackSizesSection = Ctx->getELFSection(".stack_sizes", ELF::SHT_PROGBITS, 0);
@@ -1064,6 +1081,7 @@ void MCObjectFileInfo::initMCObjectFileInfo(MCContext &MCCtx, bool PIC,
CompactUnwindDwarfEHFrameOnly = 0;
EHFrameSection = nullptr; // Created on demand.
+ SFrameSection = nullptr; // Created on demand.
CompactUnwindSection = nullptr; // Used only by selected targets.
DwarfAccelNamesSection = nullptr; // Used only by selected targets.
DwarfAccelObjCSection = nullptr; // Used only by selected targets.
diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp
index cdd6189d890f3..59265bc8595ba 100644
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -17,6 +17,7 @@
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCObjectWriter.h"
+#include "llvm/MC/MCSFrame.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/Support/ErrorHandling.h"
@@ -30,7 +31,7 @@ MCObjectStreamer::MCObjectStreamer(MCContext &Context,
: MCStreamer(Context),
Assembler(std::make_unique<MCAssembler>(
Context, std::move(TAB), std::move(Emitter), std::move(OW))),
- EmitEHFrame(true), EmitDebugFrame(false) {
+ EmitEHFrame(true), EmitDebugFrame(false), EmitSFrame(false) {
assert(Assembler->getBackendPtr() && Assembler->getEmitterPtr());
IsObj = true;
setAllowAutoPadding(Assembler->getBackend().allowAutoPadding());
@@ -186,6 +187,10 @@ void MCObjectStreamer::emitFrames(MCAsmBackend *MAB) {
if (EmitDebugFrame)
MCDwarfFrameEmitter::Emit(*this, MAB, false);
+
+ if (EmitSFrame || (getContext().getTargetOptions() &&
+ getContext().getTargetOptions()->EmitSFrameUnwind))
+ MCSFrameEmitter::emit(*this);
}
void MCObjectStreamer::visitUsedSymbol(const MCSymbol &Sym) {
diff --git a/llvm/lib/MC/MCSFrame.cpp b/llvm/lib/MC/MCSFrame.cpp
new file mode 100644
index 0000000000000..447f22eaaf17e
--- /dev/null
+++ b/llvm/lib/MC/MCSFrame.cpp
@@ -0,0 +1,98 @@
+//===- lib/MC/MCSFrame.cpp - MCSFrame implementation ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/MC/MCSFrame.h"
+#include "llvm/BinaryFormat/SFrame.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCObjectStreamer.h"
+#include "llvm/MC/MCSection.h"
+#include "llvm/MC/MCSymbol.h"
+#include "llvm/Support/EndianStream.h"
+
+using namespace llvm;
+using namespace sframe;
+
+namespace {
+
+// Emitting these field-by-field, instead of constructing the actual structures
+// lets Streamer do target endian-fixups for free.
+
+class SFrameEmitterImpl {
+ MCObjectStreamer &Streamer;
+ ABI SFrameABI;
+ MCSymbol *FDESubSectionStart;
+ MCSymbol *FRESubSectionStart;
+ MCSymbol *FRESubSectionEnd;
+
+public:
+ SFrameEmitterImpl(MCObjectStreamer &Streamer) : Streamer(Streamer) {
+ assert(Streamer.getContext()
+ .getObjectFileInfo()
+ ->getSFrameABIArch()
+ .has_value());
+ SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch();
+ FDESubSectionStart = Streamer.getContext().createTempSymbol();
+ FRESubSectionStart = Streamer.getContext().createTempSymbol();
+ FRESubSectionEnd = Streamer.getContext().createTempSymbol();
+ }
+
+ void emitPreamble() {
+ Streamer.emitInt16(Magic);
+ Streamer.emitInt8(static_cast<uint8_t>(Version::V2));
+ Streamer.emitInt8(0);
+ }
+
+ void emitHeader() {
+ emitPreamble();
+ // sfh_abi_arch
+ Streamer.emitInt8(static_cast<uint8_t>(SFrameABI));
+ // sfh_cfa_fixed_fp_offset
+ Streamer.emitInt8(0);
+ // sfh_cfa_fixed_ra_offset
+ Streamer.emitInt8(0);
+ // sfh_auxhdr_len
+ Streamer.emitInt8(0);
+ // shf_num_fdes
+ Streamer.emitInt32(0);
+ // shf_num_fres
+ Streamer.emitInt32(0);
+ // shf_fre_len
+ Streamer.emitAbsoluteSymbolDiff(FRESubSectionEnd, FRESubSectionStart,
+ sizeof(int32_t));
+ // shf_fdeoff. With no sfh_auxhdr, these immediately follow this header.
+ Streamer.emitInt32(0);
+ // shf_freoff
+ Streamer.emitAbsoluteSymbolDiff(FRESubSectionStart, FDESubSectionStart,
+ sizeof(uint32_t));
+ }
+
+ void emitFDEs() { Streamer.emitLabel(FDESubSectionStart); }
+
+ void emitFREs() {
+ Streamer.emitLabel(FRESubSectionStart);
+ Streamer.emitLabel(FRESubSectionEnd);
+ }
+};
+
+} // end anonymous namespace
+
+void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
+ MCContext &Context = Streamer.getContext();
+ SFrameEmitterImpl Emitter(Streamer);
+
+ MCSection *Section = Context.getObjectFileInfo()->getSFrameSection();
+ // Not strictly necessary, but gas always aligns to 8, so match that.
+ Section->ensureMinAlignment(Align(8));
+ Streamer.switchSection(Section);
+ MCSymbol *SectionStart = Context.createTempSymbol();
+ Streamer.emitLabel(SectionStart);
+ Emitter.emitHeader();
+ Emitter.emitFDEs();
+ Emitter.emitFREs();
+}
diff --git a/llvm/test/MC/ELF/cfi-sframe.s b/llvm/test/MC/ELF/cfi-sframe.s
new file mode 100644
index 0000000000000..45fce241c8764
--- /dev/null
+++ b/llvm/test/MC/ELF/cfi-sframe.s
@@ -0,0 +1,26 @@
+// TODO: Add other architectures as they gain sframe support
+// REQUIRES: x86-registered-target
+// RUN: llvm-mc --assemble --filetype=obj --gsframe -triple x86_64 %s -o %t.o
+// RUN: llvm-readelf --sframe %t.o | FileCheck %s
+
+ .cfi_sections .sframe
+
+f1:
+ .cfi_startproc
+ nop
+ .cfi_endproc
+
+// CHECK: SFrame section '.sframe' {
+// CHECK-NEXT: Header {
+// CHECK-NEXT: Magic: 0xDEE2
+// CHECK-NEXT: Version: V2 (0x2)
+// CHECK-NEXT: Flags [ (0x0)
+// 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 FREs: 0
+// CHECK-NEXT: FRE subsection length: 0
+// CHECK-NEXT: FDE subsection offset: 0
+// CHECK-NEXT: FRE subsection offset: 0
More information about the llvm-commits
mailing list