Skip to content

Commit 353fe37

Browse files
committed
[BinaryFormat] Add "SFrame" structures and constants
This patch defines the structures and constants used by the SFrame unwind info format supported by GNU binutils. For more information about the format, see https://sourceware.org/binutils/wiki/sframe and https://discourse.llvm.org/t/rfc-adding-sframe-support-to-llvm/86900 In the patch, I've used the naming convention for everything that has a direct equivalent to the specification (modulo changing macros to constants), and used the llvm convention for everything else.
1 parent 6d4a502 commit 353fe37

File tree

3 files changed

+284
-0
lines changed

3 files changed

+284
-0
lines changed
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
//===----------------------------------------------------------------------===//
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+
/// \file
10+
/// This file contains data-structure definitions and constants to support
11+
/// unwinding based on .sframe sections. This only supports SFRAME_VERSION_2
12+
/// as described at https://sourceware.org/binutils/docs/sframe-spec.html
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_BINARYFORMAT_SFRAME_H
16+
#define LLVM_BINARYFORMAT_SFRAME_H
17+
18+
#include "llvm/ADT/BitmaskEnum.h"
19+
#include "llvm/Support/DataTypes.h"
20+
#include "llvm/Support/Endian.h"
21+
22+
namespace llvm::sframe {
23+
24+
LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
25+
26+
constexpr uint16_t Magic = 0xdee2;
27+
28+
enum class Version : uint8_t {
29+
V1 = 1,
30+
V2 = 2,
31+
};
32+
33+
enum class Flags : uint8_t {
34+
FDESorted = 0x01,
35+
FramePointer = 0x02,
36+
FDEFuncStartPCRel = 0x04,
37+
V2AllFlags = FDESorted | FramePointer | FDEFuncStartPCRel,
38+
LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/0xff),
39+
};
40+
41+
enum class ABI : uint8_t {
42+
AArch64EndianBig = 1,
43+
AArch64EndianLittle = 2,
44+
AMD64EndianLittle = 3,
45+
};
46+
47+
/// SFrame FRE Types. Bits 0-3 of FuncDescEntry.Info.
48+
enum class FREType : uint8_t {
49+
Addr1 = 0,
50+
Addr2 = 1,
51+
Addr4 = 2,
52+
};
53+
54+
/// SFrame FDE Types. Bit 4 of FuncDescEntry.Info.
55+
enum class FDEType : uint8_t {
56+
PCInc = 0,
57+
PCMask = 1,
58+
};
59+
60+
/// Speficies key used for signing return addresses. Bit 5 of
61+
/// FuncDescEntry.Info.
62+
enum class AArch64PAuthKey : uint8_t {
63+
A = 0,
64+
B = 1,
65+
};
66+
67+
/// Size of stack offsets. Bits 5-6 of FREInfo.Info.
68+
enum class FREOffset : uint8_t {
69+
B1 = 0,
70+
B2 = 1,
71+
B4 = 2,
72+
};
73+
74+
/// Stack frame base register. Bit 0 of FREInfo.Info.
75+
enum class BaseReg : uint8_t {
76+
FP = 0,
77+
SP = 1,
78+
};
79+
80+
namespace detail {
81+
template <typename T, endianness E>
82+
using packed =
83+
support::detail::packed_endian_specific_integral<T, E, support::unaligned>;
84+
}
85+
86+
template <endianness E> struct Preamble {
87+
detail::packed<uint16_t, E> Magic;
88+
detail::packed<enum Version, E> Version;
89+
detail::packed<enum Flags, E> Flags;
90+
};
91+
92+
template <endianness E> struct Header {
93+
struct Preamble<E> Preamble;
94+
detail::packed<ABI, E> ABIArch;
95+
detail::packed<int8_t, E> CFAFixedFPOffset;
96+
detail::packed<int8_t, E> CFAFixedRAOffset;
97+
detail::packed<uint8_t, E> AuxHdrLen;
98+
detail::packed<uint32_t, E> NumFDEs;
99+
detail::packed<uint32_t, E> NumFREs;
100+
detail::packed<uint32_t, E> FRELen;
101+
detail::packed<uint32_t, E> FDEOff;
102+
detail::packed<uint32_t, E> FREOff;
103+
};
104+
105+
template <endianness E> struct FuncDescEntry {
106+
detail::packed<int32_t, E> StartAddress;
107+
detail::packed<uint32_t, E> Size;
108+
detail::packed<uint32_t, E> StartFREOff;
109+
detail::packed<uint32_t, E> NumFREs;
110+
detail::packed<uint8_t, E> Info;
111+
detail::packed<uint8_t, E> RepSize;
112+
detail::packed<uint16_t, E> Padding2;
113+
114+
uint8_t getPAuthKey() const { return (Info >> 5) & 1; }
115+
FDEType getFDEType() const { return static_cast<FDEType>((Info >> 4) & 1); }
116+
FREType getFREType() const { return static_cast<FREType>(Info & 0xf); }
117+
void setPAuthKey(uint8_t P) { setFuncInfo(P, getFDEType(), getFREType()); }
118+
void setFDEType(FDEType D) { setFuncInfo(getPAuthKey(), D, getFREType()); }
119+
void setFREType(FREType R) { setFuncInfo(getPAuthKey(), getFDEType(), R); }
120+
void setFuncInfo(uint8_t PAuthKey, FDEType FDE, FREType FRE) {
121+
Info = ((PAuthKey & 1) << 5) | ((static_cast<uint8_t>(FDE) & 1) << 4) |
122+
(static_cast<uint8_t>(FRE) & 0xf);
123+
}
124+
};
125+
126+
template <endianness E> struct FREInfo {
127+
detail::packed<uint8_t, E> Info;
128+
129+
bool isReturnAddressSigned() const { return Info >> 7; }
130+
FREOffset getOffsetSize() const {
131+
return static_cast<FREOffset>((Info >> 5) & 3);
132+
}
133+
uint8_t getOffsetCount() const { return (Info >> 1) & 0xf; }
134+
BaseReg getBaseRegister() const { return static_cast<BaseReg>(Info & 1); }
135+
void setReturnAddressSigned(bool RA) {
136+
setFREInfo(RA, getOffsetSize(), getOffsetCount(), getBaseRegister());
137+
}
138+
void setOffsetSize(FREOffset Sz) {
139+
setFREInfo(isReturnAddressSigned(), Sz, getOffsetCount(),
140+
getBaseRegister());
141+
}
142+
void setOffsetCount(uint8_t N) {
143+
setFREInfo(isReturnAddressSigned(), getOffsetSize(), N, getBaseRegister());
144+
}
145+
void setBaseRegister(BaseReg Reg) {
146+
setFREInfo(isReturnAddressSigned(), getOffsetSize(), getOffsetCount(), Reg);
147+
}
148+
void setFREInfo(bool RA, FREOffset Sz, uint8_t N, BaseReg Reg) {
149+
Info = ((RA & 1) << 7) | ((static_cast<uint8_t>(Sz) & 3) << 5) |
150+
((N & 0xf) << 1) | (static_cast<uint8_t>(Reg) & 1);
151+
}
152+
};
153+
154+
template <typename T, endianness E> struct FrameRowEntry {
155+
detail::packed<T, E> StartAddress;
156+
FREInfo<E> Info;
157+
};
158+
159+
template <endianness E> using FrameRowEntryAddr1 = FrameRowEntry<uint8_t, E>;
160+
template <endianness E> using FrameRowEntryAddr2 = FrameRowEntry<uint16_t, E>;
161+
template <endianness E> using FrameRowEntryAddr4 = FrameRowEntry<uint32_t, E>;
162+
163+
} // namespace llvm::sframe
164+
165+
#endif // LLVM_BINARYFORMAT_SFRAME_H

llvm/unittests/BinaryFormat/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_llvm_unittest(BinaryFormatTests
1010
MsgPackDocumentTest.cpp
1111
MsgPackReaderTest.cpp
1212
MsgPackWriterTest.cpp
13+
SFrameTest.cpp
1314
TestFileMagic.cpp
1415
)
1516

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//===- SFrameTest.cpp -----------------------------------------------------===//
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/BinaryFormat/SFrame.h"
10+
#include "gtest/gtest.h"
11+
#include <type_traits>
12+
13+
using namespace llvm;
14+
using namespace llvm::sframe;
15+
16+
namespace {
17+
18+
template <typename EndianT> class SFrameTest : public testing::Test {
19+
protected:
20+
static constexpr endianness Endian = EndianT::value;
21+
22+
// Test structure sizes and triviality.
23+
static_assert(std::is_trivial_v<Preamble<Endian>>);
24+
static_assert(sizeof(Preamble<Endian>) == 4);
25+
26+
static_assert(std::is_trivial_v<Header<Endian>>);
27+
static_assert(sizeof(Header<Endian>) == 28);
28+
29+
static_assert(std::is_trivial_v<FuncDescEntry<Endian>>);
30+
static_assert(sizeof(FuncDescEntry<Endian>) == 20);
31+
32+
static_assert(std::is_trivial_v<FrameRowEntryAddr1<Endian>>);
33+
static_assert(sizeof(FrameRowEntryAddr1<Endian>) == 2);
34+
35+
static_assert(std::is_trivial_v<FrameRowEntryAddr2<Endian>>);
36+
static_assert(sizeof(FrameRowEntryAddr2<Endian>) == 3);
37+
38+
static_assert(std::is_trivial_v<FrameRowEntryAddr4<Endian>>);
39+
static_assert(sizeof(FrameRowEntryAddr4<Endian>) == 5);
40+
};
41+
42+
struct NameGenerator {
43+
template <typename T> static constexpr const char *GetName(int) {
44+
if constexpr (T::value == endianness::little)
45+
return "little";
46+
if constexpr (T::value == endianness::big)
47+
return "big";
48+
}
49+
};
50+
using Types =
51+
testing::Types<std::integral_constant<endianness, endianness::little>,
52+
std::integral_constant<endianness, endianness::big>>;
53+
TYPED_TEST_SUITE(SFrameTest, Types, NameGenerator);
54+
55+
TYPED_TEST(SFrameTest, FDEFlags) {
56+
FuncDescEntry<TestFixture::Endian> FDE = {};
57+
EXPECT_EQ(FDE.Info, 0u);
58+
EXPECT_EQ(FDE.getPAuthKey(), 0);
59+
EXPECT_EQ(FDE.getFDEType(), FDEType::PCInc);
60+
EXPECT_EQ(FDE.getFREType(), FREType::Addr1);
61+
62+
FDE.setPAuthKey(1);
63+
EXPECT_EQ(FDE.Info, 0x20u);
64+
EXPECT_EQ(FDE.getPAuthKey(), 1);
65+
EXPECT_EQ(FDE.getFDEType(), FDEType::PCInc);
66+
EXPECT_EQ(FDE.getFREType(), FREType::Addr1);
67+
68+
FDE.setFDEType(FDEType::PCMask);
69+
EXPECT_EQ(FDE.Info, 0x30u);
70+
EXPECT_EQ(FDE.getPAuthKey(), 1);
71+
EXPECT_EQ(FDE.getFDEType(), FDEType::PCMask);
72+
EXPECT_EQ(FDE.getFREType(), FREType::Addr1);
73+
74+
FDE.setFREType(FREType::Addr4);
75+
EXPECT_EQ(FDE.Info, 0x32u);
76+
EXPECT_EQ(FDE.getPAuthKey(), 1);
77+
EXPECT_EQ(FDE.getFDEType(), FDEType::PCMask);
78+
EXPECT_EQ(FDE.getFREType(), FREType::Addr4);
79+
}
80+
81+
TYPED_TEST(SFrameTest, FREFlags) {
82+
FREInfo<TestFixture::Endian> Info = {};
83+
EXPECT_EQ(Info.Info, 0u);
84+
EXPECT_FALSE(Info.isReturnAddressSigned());
85+
EXPECT_EQ(Info.getOffsetSize(), FREOffset::B1);
86+
EXPECT_EQ(Info.getOffsetCount(), 0u);
87+
EXPECT_EQ(Info.getBaseRegister(), BaseReg::FP);
88+
89+
Info.setReturnAddressSigned(true);
90+
EXPECT_EQ(Info.Info, 0x80u);
91+
EXPECT_TRUE(Info.isReturnAddressSigned());
92+
EXPECT_EQ(Info.getOffsetSize(), FREOffset::B1);
93+
EXPECT_EQ(Info.getOffsetCount(), 0u);
94+
EXPECT_EQ(Info.getBaseRegister(), BaseReg::FP);
95+
96+
Info.setOffsetSize(FREOffset::B4);
97+
EXPECT_EQ(Info.Info, 0xc0u);
98+
EXPECT_TRUE(Info.isReturnAddressSigned());
99+
EXPECT_EQ(Info.getOffsetSize(), FREOffset::B4);
100+
EXPECT_EQ(Info.getOffsetCount(), 0u);
101+
EXPECT_EQ(Info.getBaseRegister(), BaseReg::FP);
102+
103+
Info.setOffsetCount(3);
104+
EXPECT_EQ(Info.Info, 0xc6u);
105+
EXPECT_TRUE(Info.isReturnAddressSigned());
106+
EXPECT_EQ(Info.getOffsetSize(), FREOffset::B4);
107+
EXPECT_EQ(Info.getOffsetCount(), 3u);
108+
EXPECT_EQ(Info.getBaseRegister(), BaseReg::FP);
109+
110+
Info.setBaseRegister(BaseReg::SP);
111+
EXPECT_EQ(Info.Info, 0xc7u);
112+
EXPECT_TRUE(Info.isReturnAddressSigned());
113+
EXPECT_EQ(Info.getOffsetSize(), FREOffset::B4);
114+
EXPECT_EQ(Info.getOffsetCount(), 3u);
115+
EXPECT_EQ(Info.getBaseRegister(), BaseReg::SP);
116+
}
117+
118+
} // namespace

0 commit comments

Comments
 (0)