Skip to content

Commit ee9b84f

Browse files
authored
[BinaryFormat] Add "SFrame" structures and constants (#147264)
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 The patch uses the GNU names for all relevant entities, but I've converted them to the llvm naming convention, and I've replaced macros with enums.
1 parent c8d0e24 commit ee9b84f

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)