aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib
diff options
context:
space:
mode:
authorSterling-Augustine <saugustine@google.com>2025-08-12 12:57:31 -0700
committerGitHub <noreply@github.com>2025-08-12 12:57:31 -0700
commit2e9944a03e6bdda64f266aa4b9fe88d81fdf16cd (patch)
treec8c14e249dacbd86b01a7ddb4d0075bbd620ecec /llvm/lib
parent35892345680b5176d34c9d0dfb65b1a7b39914af (diff)
downloadllvm-2e9944a03e6bdda64f266aa4b9fe88d81fdf16cd.zip
llvm-2e9944a03e6bdda64f266aa4b9fe88d81fdf16cd.tar.gz
llvm-2e9944a03e6bdda64f266aa4b9fe88d81fdf16cd.tar.bz2
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.
Diffstat (limited to 'llvm/lib')
-rw-r--r--llvm/lib/MC/CMakeLists.txt1
-rw-r--r--llvm/lib/MC/MCObjectFileInfo.cpp18
-rw-r--r--llvm/lib/MC/MCObjectStreamer.cpp7
-rw-r--r--llvm/lib/MC/MCSFrame.cpp98
4 files changed, 123 insertions, 1 deletions
diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt
index 18a85b3..1e1d0a6 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 4ac73ab..d274c88 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 different 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 cdd6189..59265bc 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 0000000..447f22e
--- /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();
+}