Skip to content

Commit 2e9944a

Browse files
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.
1 parent 3589234 commit 2e9944a

File tree

7 files changed

+190
-1
lines changed

7 files changed

+190
-1
lines changed

llvm/include/llvm/MC/MCObjectFileInfo.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef LLVM_MC_MCOBJECTFILEINFO_H
1414
#define LLVM_MC_MCOBJECTFILEINFO_H
1515

16+
#include "llvm/BinaryFormat/SFrame.h"
1617
#include "llvm/BinaryFormat/Swift.h"
1718
#include "llvm/MC/MCSection.h"
1819
#include "llvm/Support/Compiler.h"
@@ -50,6 +51,9 @@ class LLVM_ABI MCObjectFileInfo {
5051
/// Compact unwind encoding indicating that we should emit only an EH frame.
5152
unsigned CompactUnwindDwarfEHFrameOnly = 0;
5253

54+
/// SFrame ABI architecture byte.
55+
std::optional<sframe::ABI> SFrameABIArch = {};
56+
5357
/// Section directive for standard text.
5458
MCSection *TextSection = nullptr;
5559

@@ -177,6 +181,9 @@ class LLVM_ABI MCObjectFileInfo {
177181
/// It is initialized on demand so it can be overwritten (with uniquing).
178182
MCSection *EHFrameSection = nullptr;
179183

184+
/// SFrame section.
185+
MCSection *SFrameSection = nullptr;
186+
180187
/// Section containing metadata on function stack sizes.
181188
MCSection *StackSizesSection = nullptr;
182189

@@ -269,6 +276,7 @@ class LLVM_ABI MCObjectFileInfo {
269276
return CompactUnwindDwarfEHFrameOnly;
270277
}
271278

279+
std::optional<sframe::ABI> getSFrameABIArch() const { return SFrameABIArch; }
272280
virtual unsigned getTextSectionAlignment() const { return 4; }
273281
MCSection *getTextSection() const { return TextSection; }
274282
MCSection *getDataSection() const { return DataSection; }
@@ -450,6 +458,7 @@ class LLVM_ABI MCObjectFileInfo {
450458
MCSection *getTOCBaseSection() const { return TOCBaseSection; }
451459

452460
MCSection *getEHFrameSection() const { return EHFrameSection; }
461+
MCSection *getSFrameSection() const { return SFrameSection; }
453462

454463
bool isPositionIndependent() const { return PositionIndependent; }
455464

llvm/include/llvm/MC/MCSFrame.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===- MCSFrame.h - Machine Code SFrame support ---------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains the declaration of MCSFrameEmitter to support emitting
10+
// sframe unwinding info from .cfi_* directives. It relies on FDEs and CIEs
11+
// created for Dwarf frame info, but emits that info in a different format.
12+
//
13+
// See https://sourceware.org/binutils/docs-2.41/sframe-spec.html
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef LLVM_MC_MCSFRAME_H
17+
#define LLVM_MC_MCSFRAME_H
18+
19+
namespace llvm {
20+
21+
class MCObjectStreamer;
22+
23+
class MCSFrameEmitter {
24+
public:
25+
// Emit the sframe section.
26+
//
27+
// \param Streamer - Emit into this stream.
28+
static void emit(MCObjectStreamer &Streamer);
29+
};
30+
31+
} // end namespace llvm
32+
#endif // LLVM_MC_MCSFRAME_H

llvm/lib/MC/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ add_llvm_component_library(LLVMMC
4545
MCSection.cpp
4646
MCSectionMachO.cpp
4747
MCStreamer.cpp
48+
MCSFrame.cpp
4849
MCSPIRVStreamer.cpp
4950
MCSubtargetInfo.cpp
5051
MCSymbol.cpp

llvm/lib/MC/MCObjectFileInfo.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "llvm/ADT/StringExtras.h"
1111
#include "llvm/BinaryFormat/COFF.h"
1212
#include "llvm/BinaryFormat/ELF.h"
13+
#include "llvm/BinaryFormat/SFrame.h"
1314
#include "llvm/BinaryFormat/Wasm.h"
1415
#include "llvm/MC/MCAsmInfo.h"
1516
#include "llvm/MC/MCContext.h"
@@ -380,6 +381,19 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) {
380381
unsigned EHSectionType = T.getArch() == Triple::x86_64
381382
? ELF::SHT_X86_64_UNWIND
382383
: ELF::SHT_PROGBITS;
384+
switch (T.getArch()) {
385+
case Triple::x86_64:
386+
SFrameABIArch = sframe::ABI::AMD64EndianLittle;
387+
break;
388+
case Triple::aarch64:
389+
SFrameABIArch = sframe::ABI::AArch64EndianLittle;
390+
break;
391+
case Triple::aarch64_be:
392+
SFrameABIArch = sframe::ABI::AArch64EndianBig;
393+
break;
394+
default:
395+
break;
396+
}
383397

384398
// Solaris requires different flags for .eh_frame to seemingly every other
385399
// platform.
@@ -537,6 +551,9 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) {
537551
EHFrameSection =
538552
Ctx->getELFSection(".eh_frame", EHSectionType, EHSectionFlags);
539553

554+
SFrameSection =
555+
Ctx->getELFSection(".sframe", ELF::SHT_GNU_SFRAME, ELF::SHF_ALLOC);
556+
540557
CallGraphSection = Ctx->getELFSection(".callgraph", ELF::SHT_PROGBITS, 0);
541558

542559
StackSizesSection = Ctx->getELFSection(".stack_sizes", ELF::SHT_PROGBITS, 0);
@@ -1064,6 +1081,7 @@ void MCObjectFileInfo::initMCObjectFileInfo(MCContext &MCCtx, bool PIC,
10641081
CompactUnwindDwarfEHFrameOnly = 0;
10651082

10661083
EHFrameSection = nullptr; // Created on demand.
1084+
SFrameSection = nullptr; // Created on demand.
10671085
CompactUnwindSection = nullptr; // Used only by selected targets.
10681086
DwarfAccelNamesSection = nullptr; // Used only by selected targets.
10691087
DwarfAccelObjCSection = nullptr; // Used only by selected targets.

llvm/lib/MC/MCObjectStreamer.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "llvm/MC/MCExpr.h"
1818
#include "llvm/MC/MCObjectFileInfo.h"
1919
#include "llvm/MC/MCObjectWriter.h"
20+
#include "llvm/MC/MCSFrame.h"
2021
#include "llvm/MC/MCSection.h"
2122
#include "llvm/MC/MCSymbol.h"
2223
#include "llvm/Support/ErrorHandling.h"
@@ -30,7 +31,7 @@ MCObjectStreamer::MCObjectStreamer(MCContext &Context,
3031
: MCStreamer(Context),
3132
Assembler(std::make_unique<MCAssembler>(
3233
Context, std::move(TAB), std::move(Emitter), std::move(OW))),
33-
EmitEHFrame(true), EmitDebugFrame(false) {
34+
EmitEHFrame(true), EmitDebugFrame(false), EmitSFrame(false) {
3435
assert(Assembler->getBackendPtr() && Assembler->getEmitterPtr());
3536
IsObj = true;
3637
setAllowAutoPadding(Assembler->getBackend().allowAutoPadding());
@@ -186,6 +187,10 @@ void MCObjectStreamer::emitFrames(MCAsmBackend *MAB) {
186187

187188
if (EmitDebugFrame)
188189
MCDwarfFrameEmitter::Emit(*this, MAB, false);
190+
191+
if (EmitSFrame || (getContext().getTargetOptions() &&
192+
getContext().getTargetOptions()->EmitSFrameUnwind))
193+
MCSFrameEmitter::emit(*this);
189194
}
190195

191196
void MCObjectStreamer::visitUsedSymbol(const MCSymbol &Sym) {

llvm/lib/MC/MCSFrame.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//===- lib/MC/MCSFrame.cpp - MCSFrame implementation ----------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/MC/MCSFrame.h"
10+
#include "llvm/BinaryFormat/SFrame.h"
11+
#include "llvm/MC/MCContext.h"
12+
#include "llvm/MC/MCObjectFileInfo.h"
13+
#include "llvm/MC/MCObjectStreamer.h"
14+
#include "llvm/MC/MCSection.h"
15+
#include "llvm/MC/MCSymbol.h"
16+
#include "llvm/Support/EndianStream.h"
17+
18+
using namespace llvm;
19+
using namespace sframe;
20+
21+
namespace {
22+
23+
// Emitting these field-by-field, instead of constructing the actual structures
24+
// lets Streamer do target endian-fixups for free.
25+
26+
class SFrameEmitterImpl {
27+
MCObjectStreamer &Streamer;
28+
ABI SFrameABI;
29+
MCSymbol *FDESubSectionStart;
30+
MCSymbol *FRESubSectionStart;
31+
MCSymbol *FRESubSectionEnd;
32+
33+
public:
34+
SFrameEmitterImpl(MCObjectStreamer &Streamer) : Streamer(Streamer) {
35+
assert(Streamer.getContext()
36+
.getObjectFileInfo()
37+
->getSFrameABIArch()
38+
.has_value());
39+
SFrameABI = *Streamer.getContext().getObjectFileInfo()->getSFrameABIArch();
40+
FDESubSectionStart = Streamer.getContext().createTempSymbol();
41+
FRESubSectionStart = Streamer.getContext().createTempSymbol();
42+
FRESubSectionEnd = Streamer.getContext().createTempSymbol();
43+
}
44+
45+
void emitPreamble() {
46+
Streamer.emitInt16(Magic);
47+
Streamer.emitInt8(static_cast<uint8_t>(Version::V2));
48+
Streamer.emitInt8(0);
49+
}
50+
51+
void emitHeader() {
52+
emitPreamble();
53+
// sfh_abi_arch
54+
Streamer.emitInt8(static_cast<uint8_t>(SFrameABI));
55+
// sfh_cfa_fixed_fp_offset
56+
Streamer.emitInt8(0);
57+
// sfh_cfa_fixed_ra_offset
58+
Streamer.emitInt8(0);
59+
// sfh_auxhdr_len
60+
Streamer.emitInt8(0);
61+
// shf_num_fdes
62+
Streamer.emitInt32(0);
63+
// shf_num_fres
64+
Streamer.emitInt32(0);
65+
// shf_fre_len
66+
Streamer.emitAbsoluteSymbolDiff(FRESubSectionEnd, FRESubSectionStart,
67+
sizeof(int32_t));
68+
// shf_fdeoff. With no sfh_auxhdr, these immediately follow this header.
69+
Streamer.emitInt32(0);
70+
// shf_freoff
71+
Streamer.emitAbsoluteSymbolDiff(FRESubSectionStart, FDESubSectionStart,
72+
sizeof(uint32_t));
73+
}
74+
75+
void emitFDEs() { Streamer.emitLabel(FDESubSectionStart); }
76+
77+
void emitFREs() {
78+
Streamer.emitLabel(FRESubSectionStart);
79+
Streamer.emitLabel(FRESubSectionEnd);
80+
}
81+
};
82+
83+
} // end anonymous namespace
84+
85+
void MCSFrameEmitter::emit(MCObjectStreamer &Streamer) {
86+
MCContext &Context = Streamer.getContext();
87+
SFrameEmitterImpl Emitter(Streamer);
88+
89+
MCSection *Section = Context.getObjectFileInfo()->getSFrameSection();
90+
// Not strictly necessary, but gas always aligns to 8, so match that.
91+
Section->ensureMinAlignment(Align(8));
92+
Streamer.switchSection(Section);
93+
MCSymbol *SectionStart = Context.createTempSymbol();
94+
Streamer.emitLabel(SectionStart);
95+
Emitter.emitHeader();
96+
Emitter.emitFDEs();
97+
Emitter.emitFREs();
98+
}

llvm/test/MC/ELF/cfi-sframe.s

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// TODO: Add other architectures as they gain sframe support
2+
// REQUIRES: x86-registered-target
3+
// RUN: llvm-mc --assemble --filetype=obj --gsframe -triple x86_64 %s -o %t.o
4+
// RUN: llvm-readelf --sframe %t.o | FileCheck %s
5+
6+
.cfi_sections .sframe
7+
8+
f1:
9+
.cfi_startproc
10+
nop
11+
.cfi_endproc
12+
13+
// CHECK: SFrame section '.sframe' {
14+
// CHECK-NEXT: Header {
15+
// CHECK-NEXT: Magic: 0xDEE2
16+
// CHECK-NEXT: Version: V2 (0x2)
17+
// CHECK-NEXT: Flags [ (0x0)
18+
// CHECK: ABI: AMD64EndianLittle (0x3)
19+
// CHECK-NEXT: CFA fixed FP offset (unused): 0
20+
// CHECK-NEXT: CFA fixed RA offset: 0
21+
// CHECK-NEXT: Auxiliary header length: 0
22+
// CHECK-NEXT: Num FDEs: 0
23+
// CHECK-NEXT: Num FREs: 0
24+
// CHECK-NEXT: FRE subsection length: 0
25+
// CHECK-NEXT: FDE subsection offset: 0
26+
// CHECK-NEXT: FRE subsection offset: 0

0 commit comments

Comments
 (0)