diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h index 5ce58ae0a836f..fa385c3124c62 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 SFrameABIArch = {}; + /// Section directive for standard text. MCSection *TextSection = nullptr; @@ -174,6 +178,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; @@ -266,6 +273,7 @@ class LLVM_ABI MCObjectFileInfo { return CompactUnwindDwarfEHFrameOnly; } + std::optional getSFrameABIArch() const { return SFrameABIArch; } virtual unsigned getTextSectionAlignment() const { return 4; } MCSection *getTextSection() const { return TextSection; } MCSection *getDataSection() const { return DataSection; } @@ -445,6 +453,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..0853806e10f59 --- /dev/null +++ b/llvm/include/llvm/MC/MCSFrame.h @@ -0,0 +1,35 @@ +//===- 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 different format. +// +// See https://sourceware.org/binutils/docs-2.41/sframe-spec.html +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCSFRAME_H +#define LLVM_MC_MCSFRAME_H + +#include "llvm/ADT/SmallVector.h" +#include + +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 0069d12dce396..2280e060861be 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); + StackSizesSection = Ctx->getELFSection(".stack_sizes", ELF::SHT_PROGBITS, 0); PseudoProbeSection = Ctx->getELFSection(".pseudo_probe", DebugSecType, 0); @@ -1062,6 +1079,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 e82393a8477ea..8059582301f1c 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( 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()); @@ -185,6 +186,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..d2c89c0e9f53b --- /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(Version::V2)); + Streamer.emitInt8(0); + } + + void EmitHeader() { + EmitPreamble(); + // sfh_abi_arch + Streamer.emitInt8(static_cast(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