diff --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h new file mode 100644 index 0000000000000..16d3b16c6c2d3 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/SFrame.h @@ -0,0 +1,165 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file contains data-structure definitions and constants to support +/// unwinding based on .sframe sections. This only supports SFRAME_VERSION_2 +/// as described at https://sourceware.org/binutils/docs/sframe-spec.html +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BINARYFORMAT_SFRAME_H +#define LLVM_BINARYFORMAT_SFRAME_H + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Endian.h" + +namespace llvm::sframe { + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +constexpr uint16_t Magic = 0xdee2; + +enum class Version : uint8_t { + V1 = 1, + V2 = 2, +}; + +enum class Flags : uint8_t { + FDESorted = 0x01, + FramePointer = 0x02, + FDEFuncStartPCRel = 0x04, + V2AllFlags = FDESorted | FramePointer | FDEFuncStartPCRel, + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/0xff), +}; + +enum class ABI : uint8_t { + AArch64EndianBig = 1, + AArch64EndianLittle = 2, + AMD64EndianLittle = 3, +}; + +/// SFrame FRE Types. Bits 0-3 of FuncDescEntry.Info. +enum class FREType : uint8_t { + Addr1 = 0, + Addr2 = 1, + Addr4 = 2, +}; + +/// SFrame FDE Types. Bit 4 of FuncDescEntry.Info. +enum class FDEType : uint8_t { + PCInc = 0, + PCMask = 1, +}; + +/// Speficies key used for signing return addresses. Bit 5 of +/// FuncDescEntry.Info. +enum class AArch64PAuthKey : uint8_t { + A = 0, + B = 1, +}; + +/// Size of stack offsets. Bits 5-6 of FREInfo.Info. +enum class FREOffset : uint8_t { + B1 = 0, + B2 = 1, + B4 = 2, +}; + +/// Stack frame base register. Bit 0 of FREInfo.Info. +enum class BaseReg : uint8_t { + FP = 0, + SP = 1, +}; + +namespace detail { +template +using packed = + support::detail::packed_endian_specific_integral; +} + +template struct Preamble { + detail::packed Magic; + detail::packed Version; + detail::packed Flags; +}; + +template struct Header { + struct Preamble Preamble; + detail::packed ABIArch; + detail::packed CFAFixedFPOffset; + detail::packed CFAFixedRAOffset; + detail::packed AuxHdrLen; + detail::packed NumFDEs; + detail::packed NumFREs; + detail::packed FRELen; + detail::packed FDEOff; + detail::packed FREOff; +}; + +template struct FuncDescEntry { + detail::packed StartAddress; + detail::packed Size; + detail::packed StartFREOff; + detail::packed NumFREs; + detail::packed Info; + detail::packed RepSize; + detail::packed Padding2; + + uint8_t getPAuthKey() const { return (Info >> 5) & 1; } + FDEType getFDEType() const { return static_cast((Info >> 4) & 1); } + FREType getFREType() const { return static_cast(Info & 0xf); } + void setPAuthKey(uint8_t P) { setFuncInfo(P, getFDEType(), getFREType()); } + void setFDEType(FDEType D) { setFuncInfo(getPAuthKey(), D, getFREType()); } + void setFREType(FREType R) { setFuncInfo(getPAuthKey(), getFDEType(), R); } + void setFuncInfo(uint8_t PAuthKey, FDEType FDE, FREType FRE) { + Info = ((PAuthKey & 1) << 5) | ((static_cast(FDE) & 1) << 4) | + (static_cast(FRE) & 0xf); + } +}; + +template struct FREInfo { + detail::packed Info; + + bool isReturnAddressSigned() const { return Info >> 7; } + FREOffset getOffsetSize() const { + return static_cast((Info >> 5) & 3); + } + uint8_t getOffsetCount() const { return (Info >> 1) & 0xf; } + BaseReg getBaseRegister() const { return static_cast(Info & 1); } + void setReturnAddressSigned(bool RA) { + setFREInfo(RA, getOffsetSize(), getOffsetCount(), getBaseRegister()); + } + void setOffsetSize(FREOffset Sz) { + setFREInfo(isReturnAddressSigned(), Sz, getOffsetCount(), + getBaseRegister()); + } + void setOffsetCount(uint8_t N) { + setFREInfo(isReturnAddressSigned(), getOffsetSize(), N, getBaseRegister()); + } + void setBaseRegister(BaseReg Reg) { + setFREInfo(isReturnAddressSigned(), getOffsetSize(), getOffsetCount(), Reg); + } + void setFREInfo(bool RA, FREOffset Sz, uint8_t N, BaseReg Reg) { + Info = ((RA & 1) << 7) | ((static_cast(Sz) & 3) << 5) | + ((N & 0xf) << 1) | (static_cast(Reg) & 1); + } +}; + +template struct FrameRowEntry { + detail::packed StartAddress; + FREInfo Info; +}; + +template using FrameRowEntryAddr1 = FrameRowEntry; +template using FrameRowEntryAddr2 = FrameRowEntry; +template using FrameRowEntryAddr4 = FrameRowEntry; + +} // namespace llvm::sframe + +#endif // LLVM_BINARYFORMAT_SFRAME_H diff --git a/llvm/unittests/BinaryFormat/CMakeLists.txt b/llvm/unittests/BinaryFormat/CMakeLists.txt index 40d3bc4dca0b6..eac5977a2c1c3 100644 --- a/llvm/unittests/BinaryFormat/CMakeLists.txt +++ b/llvm/unittests/BinaryFormat/CMakeLists.txt @@ -10,6 +10,7 @@ add_llvm_unittest(BinaryFormatTests MsgPackDocumentTest.cpp MsgPackReaderTest.cpp MsgPackWriterTest.cpp + SFrameTest.cpp TestFileMagic.cpp ) diff --git a/llvm/unittests/BinaryFormat/SFrameTest.cpp b/llvm/unittests/BinaryFormat/SFrameTest.cpp new file mode 100644 index 0000000000000..394e382e041e9 --- /dev/null +++ b/llvm/unittests/BinaryFormat/SFrameTest.cpp @@ -0,0 +1,118 @@ +//===- SFrameTest.cpp -----------------------------------------------------===// +// +// 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/BinaryFormat/SFrame.h" +#include "gtest/gtest.h" +#include + +using namespace llvm; +using namespace llvm::sframe; + +namespace { + +template class SFrameTest : public testing::Test { +protected: + static constexpr endianness Endian = EndianT::value; + + // Test structure sizes and triviality. + static_assert(std::is_trivial_v>); + static_assert(sizeof(Preamble) == 4); + + static_assert(std::is_trivial_v>); + static_assert(sizeof(Header) == 28); + + static_assert(std::is_trivial_v>); + static_assert(sizeof(FuncDescEntry) == 20); + + static_assert(std::is_trivial_v>); + static_assert(sizeof(FrameRowEntryAddr1) == 2); + + static_assert(std::is_trivial_v>); + static_assert(sizeof(FrameRowEntryAddr2) == 3); + + static_assert(std::is_trivial_v>); + static_assert(sizeof(FrameRowEntryAddr4) == 5); +}; + +struct NameGenerator { + template static constexpr const char *GetName(int) { + if constexpr (T::value == endianness::little) + return "little"; + if constexpr (T::value == endianness::big) + return "big"; + } +}; +using Types = + testing::Types, + std::integral_constant>; +TYPED_TEST_SUITE(SFrameTest, Types, NameGenerator); + +TYPED_TEST(SFrameTest, FDEFlags) { + FuncDescEntry FDE = {}; + EXPECT_EQ(FDE.Info, 0u); + EXPECT_EQ(FDE.getPAuthKey(), 0); + EXPECT_EQ(FDE.getFDEType(), FDEType::PCInc); + EXPECT_EQ(FDE.getFREType(), FREType::Addr1); + + FDE.setPAuthKey(1); + EXPECT_EQ(FDE.Info, 0x20u); + EXPECT_EQ(FDE.getPAuthKey(), 1); + EXPECT_EQ(FDE.getFDEType(), FDEType::PCInc); + EXPECT_EQ(FDE.getFREType(), FREType::Addr1); + + FDE.setFDEType(FDEType::PCMask); + EXPECT_EQ(FDE.Info, 0x30u); + EXPECT_EQ(FDE.getPAuthKey(), 1); + EXPECT_EQ(FDE.getFDEType(), FDEType::PCMask); + EXPECT_EQ(FDE.getFREType(), FREType::Addr1); + + FDE.setFREType(FREType::Addr4); + EXPECT_EQ(FDE.Info, 0x32u); + EXPECT_EQ(FDE.getPAuthKey(), 1); + EXPECT_EQ(FDE.getFDEType(), FDEType::PCMask); + EXPECT_EQ(FDE.getFREType(), FREType::Addr4); +} + +TYPED_TEST(SFrameTest, FREFlags) { + FREInfo Info = {}; + EXPECT_EQ(Info.Info, 0u); + EXPECT_FALSE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), FREOffset::B1); + EXPECT_EQ(Info.getOffsetCount(), 0u); + EXPECT_EQ(Info.getBaseRegister(), BaseReg::FP); + + Info.setReturnAddressSigned(true); + EXPECT_EQ(Info.Info, 0x80u); + EXPECT_TRUE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), FREOffset::B1); + EXPECT_EQ(Info.getOffsetCount(), 0u); + EXPECT_EQ(Info.getBaseRegister(), BaseReg::FP); + + Info.setOffsetSize(FREOffset::B4); + EXPECT_EQ(Info.Info, 0xc0u); + EXPECT_TRUE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), FREOffset::B4); + EXPECT_EQ(Info.getOffsetCount(), 0u); + EXPECT_EQ(Info.getBaseRegister(), BaseReg::FP); + + Info.setOffsetCount(3); + EXPECT_EQ(Info.Info, 0xc6u); + EXPECT_TRUE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), FREOffset::B4); + EXPECT_EQ(Info.getOffsetCount(), 3u); + EXPECT_EQ(Info.getBaseRegister(), BaseReg::FP); + + Info.setBaseRegister(BaseReg::SP); + EXPECT_EQ(Info.Info, 0xc7u); + EXPECT_TRUE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), FREOffset::B4); + EXPECT_EQ(Info.getOffsetCount(), 3u); + EXPECT_EQ(Info.getBaseRegister(), BaseReg::SP); +} + +} // namespace