Skip to content

Commit 9a7e6a5

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 67be4fe commit 9a7e6a5

File tree

3 files changed

+266
-0
lines changed

3 files changed

+266
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//===-- llvm/BinaryFormat/SFrame.h ---SFrame Data Structures ----*- C++ -*-===//
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+
/// Naming conventions follow the spec document. #defines converted to constants
15+
/// and enums for better C++ compatibility.
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef LLVM_BINARYFORMAT_SFRAME_H
19+
#define LLVM_BINARYFORMAT_SFRAME_H
20+
21+
#include "llvm/Support/Compiler.h"
22+
#include "llvm/Support/DataTypes.h"
23+
24+
namespace llvm {
25+
26+
namespace sframe {
27+
28+
//===----------------------------------------------------------------------===//
29+
30+
constexpr uint16_t SFRAME_MAGIC = 0xDEE2;
31+
32+
enum : uint8_t {
33+
SFRAME_VERSION_1 = 1,
34+
SFRAME_VERSION_2 = 2,
35+
};
36+
37+
/// sframe_preable.sfp_flags flags.
38+
enum : uint8_t {
39+
SFRAME_F_FDE_SORTED = 0x1,
40+
SFRAME_F_FRAME_POINTER = 0x2,
41+
};
42+
43+
/// Possible values for sframe_header.sfh_abi_arch.
44+
enum : uint8_t {
45+
SFRAME_ABI_AARCH64_ENDIAN_BIG = 1,
46+
SFRAME_ABI_AARCH64_ENDIAN_LITTLE = 2,
47+
SFRAME_ABI_AMD64_ENDIAN_LITTLE = 3
48+
};
49+
50+
/// SFrame FRE Types. Bits 0-3 of sframe_func_desc_entry.sfde_func_info.
51+
enum : uint8_t {
52+
SFRAME_FRE_TYPE_ADDR1 = 0,
53+
SFRAME_FRE_TYPE_ADDR2 = 1,
54+
SFRAME_FRE_TYPE_ADDR4 = 2,
55+
};
56+
57+
/// SFrame FDE Types. Bit 4 of sframe_func_desc_entry.sfde_func_info.
58+
enum : uint8_t {
59+
SFRAME_FDE_TYPE_PCINC = 0,
60+
SFRAME_FDE_TYPE_PCMASK = 1,
61+
};
62+
63+
/// Speficies key used for signing return addresses. Bit 5 of
64+
/// sframe_func_desc_entry.sfde_func_info.
65+
enum : uint8_t {
66+
SFRAME_AARCH64_PAUTH_KEY_A = 0,
67+
SFRAME_AARCH64_PAUTH_KEY_B = 1,
68+
};
69+
70+
/// Size of stack offsets. Bits 5-6 of sframe_fre_info.fre_info.
71+
enum : uint8_t {
72+
SFRAME_FRE_OFFSET_1B = 0,
73+
SFRAME_FRE_OFFSET_2B = 1,
74+
SFRAME_FRE_OFFSET_4B = 2,
75+
};
76+
77+
/// Stack frame base register. Bit 0 of sframe_fre_info.fre_info.
78+
enum : uint8_t { SFRAME_BASE_REG_FP = 0, SFRAME_BASE_REG_SP = 1 };
79+
80+
LLVM_PACKED_START
81+
82+
struct sframe_preamble {
83+
uint16_t sfp_magic;
84+
uint8_t sfp_version;
85+
uint8_t sfp_flags;
86+
};
87+
88+
struct sframe_header {
89+
sframe_preamble sfh_preamble;
90+
uint8_t sfh_abi_arch;
91+
int8_t sfh_cfa_fixed_fp_offset;
92+
int8_t sfh_cfa_fixed_ra_offset;
93+
uint8_t sfh_auxhdr_len;
94+
uint32_t sfh_num_fdes;
95+
uint32_t sfh_num_fres;
96+
uint32_t sfh_fre_len;
97+
uint32_t sfh_fdeoff;
98+
uint32_t sfh_freoff;
99+
};
100+
101+
struct sframe_func_desc_entry {
102+
int32_t sfde_func_start_address;
103+
uint32_t sfde_func_size;
104+
uint32_t sfde_func_start_fre_off;
105+
uint32_t sfde_func_num_fres;
106+
uint8_t sfde_func_info;
107+
uint8_t sfde_func_rep_size;
108+
uint16_t sfde_func_padding2;
109+
110+
uint8_t getPAuthKey() const { return (sfde_func_info >> 5) & 1; }
111+
uint8_t getFDEType() const { return (sfde_func_info >> 4) & 1; }
112+
uint8_t getFREType() const { return sfde_func_info & 0xf; }
113+
void setPAuthKey(uint8_t P) { setFuncInfo(P, getFDEType(), getFREType()); }
114+
void setFDEType(uint8_t D) { setFuncInfo(getPAuthKey(), D, getFREType()); }
115+
void setFREType(uint8_t R) { setFuncInfo(getPAuthKey(), getFDEType(), R); }
116+
void setFuncInfo(uint8_t PAuthKey, uint8_t FDEType, uint8_t FREType) {
117+
sfde_func_info =
118+
((PAuthKey & 1) << 5) | ((FDEType & 1) << 4) | (FREType & 0xf);
119+
}
120+
};
121+
122+
struct sframe_fre_info {
123+
uint8_t fre_info;
124+
125+
bool isReturnAddressSigned() const { return fre_info >> 7; }
126+
uint8_t getOffsetSize() const { return (fre_info >> 5) & 3; }
127+
uint8_t getOffsetCount() const { return (fre_info >> 1) & 0xf; }
128+
uint8_t getBaseRegister() const { return fre_info & 1; }
129+
void setReturnAddressSigned(bool RA) {
130+
setFREInfo(RA, getOffsetSize(), getOffsetCount(), getBaseRegister());
131+
}
132+
void setOffsetSize(uint8_t Sz) {
133+
setFREInfo(isReturnAddressSigned(), Sz, getOffsetCount(),
134+
getBaseRegister());
135+
}
136+
void setOffsetCount(uint8_t N) {
137+
setFREInfo(isReturnAddressSigned(), getOffsetSize(), N, getBaseRegister());
138+
}
139+
void setBaseRegister(uint8_t Reg) {
140+
setFREInfo(isReturnAddressSigned(), getOffsetSize(), getOffsetCount(), Reg);
141+
}
142+
void setFREInfo(bool RA, uint8_t Sz, uint8_t N, uint8_t Reg) {
143+
fre_info = ((RA & 1) << 7) | ((Sz & 3) << 5) | ((N & 0xf) << 1) | (Reg & 1);
144+
}
145+
};
146+
147+
struct sframe_frame_row_entry_addr1 {
148+
uint8_t sfre_start_address;
149+
sframe_fre_info sfre_info;
150+
};
151+
152+
struct sframe_frame_row_entry_addr2 {
153+
uint16_t sfre_start_address;
154+
sframe_fre_info sfre_info;
155+
};
156+
157+
struct sframe_frame_row_entry_addr4 {
158+
uint32_t sfre_start_address;
159+
sframe_fre_info sfre_info;
160+
};
161+
162+
LLVM_PACKED_END
163+
164+
} // namespace sframe
165+
} // namespace llvm
166+
167+
#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: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
12+
using namespace llvm;
13+
using namespace llvm::sframe;
14+
15+
namespace {
16+
// Test structure sizes and triviality.
17+
static_assert(std::is_trivial_v<sframe_preamble>);
18+
static_assert(sizeof(sframe_preamble) == 4);
19+
20+
static_assert(std::is_trivial_v<sframe_header>);
21+
static_assert(sizeof(sframe_header) == 28);
22+
23+
static_assert(std::is_trivial_v<sframe_func_desc_entry>);
24+
static_assert(sizeof(sframe_func_desc_entry) == 20);
25+
26+
static_assert(std::is_trivial_v<sframe_frame_row_entry_addr1>);
27+
static_assert(sizeof(sframe_frame_row_entry_addr1) == 2);
28+
29+
static_assert(std::is_trivial_v<sframe_frame_row_entry_addr2>);
30+
static_assert(sizeof(sframe_frame_row_entry_addr2) == 3);
31+
32+
static_assert(std::is_trivial_v<sframe_frame_row_entry_addr4>);
33+
static_assert(sizeof(sframe_frame_row_entry_addr4) == 5);
34+
35+
TEST(SFrameTest, FDEFlags) {
36+
sframe_func_desc_entry FDE = {};
37+
EXPECT_EQ(FDE.sfde_func_info, 0u);
38+
EXPECT_EQ(FDE.getPAuthKey(), SFRAME_AARCH64_PAUTH_KEY_A);
39+
EXPECT_EQ(FDE.getFDEType(), SFRAME_FDE_TYPE_PCINC);
40+
EXPECT_EQ(FDE.getFREType(), SFRAME_FRE_TYPE_ADDR1);
41+
42+
FDE.setPAuthKey(SFRAME_AARCH64_PAUTH_KEY_B);
43+
EXPECT_EQ(FDE.sfde_func_info, 0x20u);
44+
EXPECT_EQ(FDE.getPAuthKey(), SFRAME_AARCH64_PAUTH_KEY_B);
45+
EXPECT_EQ(FDE.getFDEType(), SFRAME_FDE_TYPE_PCINC);
46+
EXPECT_EQ(FDE.getFREType(), SFRAME_FRE_TYPE_ADDR1);
47+
48+
FDE.setFDEType(SFRAME_FDE_TYPE_PCMASK);
49+
EXPECT_EQ(FDE.sfde_func_info, 0x30u);
50+
EXPECT_EQ(FDE.getPAuthKey(), SFRAME_AARCH64_PAUTH_KEY_B);
51+
EXPECT_EQ(FDE.getFDEType(), SFRAME_FDE_TYPE_PCMASK);
52+
EXPECT_EQ(FDE.getFREType(), SFRAME_FRE_TYPE_ADDR1);
53+
54+
FDE.setFREType(SFRAME_FRE_TYPE_ADDR4);
55+
EXPECT_EQ(FDE.sfde_func_info, 0x32u);
56+
EXPECT_EQ(FDE.getPAuthKey(), SFRAME_AARCH64_PAUTH_KEY_B);
57+
EXPECT_EQ(FDE.getFDEType(), SFRAME_FDE_TYPE_PCMASK);
58+
EXPECT_EQ(FDE.getFREType(), SFRAME_FRE_TYPE_ADDR4);
59+
}
60+
61+
TEST(SFrameTest, FREFlags) {
62+
sframe_fre_info Info = {};
63+
EXPECT_EQ(Info.fre_info, 0u);
64+
EXPECT_FALSE(Info.isReturnAddressSigned());
65+
EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_1B);
66+
EXPECT_EQ(Info.getOffsetCount(), 0u);
67+
EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_FP);
68+
69+
Info.setReturnAddressSigned(true);
70+
EXPECT_EQ(Info.fre_info, 0x80u);
71+
EXPECT_TRUE(Info.isReturnAddressSigned());
72+
EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_1B);
73+
EXPECT_EQ(Info.getOffsetCount(), 0u);
74+
EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_FP);
75+
76+
Info.setOffsetSize(SFRAME_FRE_OFFSET_4B);
77+
EXPECT_EQ(Info.fre_info, 0xc0u);
78+
EXPECT_TRUE(Info.isReturnAddressSigned());
79+
EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_4B);
80+
EXPECT_EQ(Info.getOffsetCount(), 0u);
81+
EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_FP);
82+
83+
Info.setOffsetCount(3);
84+
EXPECT_EQ(Info.fre_info, 0xc6u);
85+
EXPECT_TRUE(Info.isReturnAddressSigned());
86+
EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_4B);
87+
EXPECT_EQ(Info.getOffsetCount(), 3u);
88+
EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_FP);
89+
90+
Info.setBaseRegister(SFRAME_BASE_REG_SP);
91+
EXPECT_EQ(Info.fre_info, 0xc7u);
92+
EXPECT_TRUE(Info.isReturnAddressSigned());
93+
EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_4B);
94+
EXPECT_EQ(Info.getOffsetCount(), 3u);
95+
EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_SP);
96+
}
97+
98+
} // namespace

0 commit comments

Comments
 (0)