From fbd23f819e5f62fb1c1719f8c68d7f205db00db7 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Fri, 4 Jul 2025 12:09:49 +0200 Subject: [PATCH 1/2] [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. --- llvm/include/llvm/BinaryFormat/SFrame.h | 165 +++++++++++++++++++++ llvm/unittests/BinaryFormat/CMakeLists.txt | 1 + llvm/unittests/BinaryFormat/SFrameTest.cpp | 98 ++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 llvm/include/llvm/BinaryFormat/SFrame.h create mode 100644 llvm/unittests/BinaryFormat/SFrameTest.cpp diff --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h new file mode 100644 index 0000000000000..c73553756bc23 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/SFrame.h @@ -0,0 +1,165 @@ +//===-- llvm/BinaryFormat/SFrame.h ---SFrame Data Structures ----*- C++ -*-===// +// +// 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 +/// +/// Naming conventions follow the spec document. #defines converted to constants +/// and enums for better C++ compatibility. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_BINARYFORMAT_SFRAME_H +#define LLVM_BINARYFORMAT_SFRAME_H + +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DataTypes.h" + +namespace llvm { + +namespace sframe { + +constexpr uint16_t SFRAME_MAGIC = 0xDEE2; + +enum : uint8_t { + SFRAME_VERSION_1 = 1, + SFRAME_VERSION_2 = 2, +}; + +/// sframe_preable.sfp_flags flags. +enum : uint8_t { + SFRAME_F_FDE_SORTED = 0x1, + SFRAME_F_FRAME_POINTER = 0x2, +}; + +/// Possible values for sframe_header.sfh_abi_arch. +enum : uint8_t { + SFRAME_ABI_AARCH64_ENDIAN_BIG = 1, + SFRAME_ABI_AARCH64_ENDIAN_LITTLE = 2, + SFRAME_ABI_AMD64_ENDIAN_LITTLE = 3 +}; + +/// SFrame FRE Types. Bits 0-3 of sframe_func_desc_entry.sfde_func_info. +enum : uint8_t { + SFRAME_FRE_TYPE_ADDR1 = 0, + SFRAME_FRE_TYPE_ADDR2 = 1, + SFRAME_FRE_TYPE_ADDR4 = 2, +}; + +/// SFrame FDE Types. Bit 4 of sframe_func_desc_entry.sfde_func_info. +enum : uint8_t { + SFRAME_FDE_TYPE_PCINC = 0, + SFRAME_FDE_TYPE_PCMASK = 1, +}; + +/// Speficies key used for signing return addresses. Bit 5 of +/// sframe_func_desc_entry.sfde_func_info. +enum : uint8_t { + SFRAME_AARCH64_PAUTH_KEY_A = 0, + SFRAME_AARCH64_PAUTH_KEY_B = 1, +}; + +/// Size of stack offsets. Bits 5-6 of sframe_fre_info.fre_info. +enum : uint8_t { + SFRAME_FRE_OFFSET_1B = 0, + SFRAME_FRE_OFFSET_2B = 1, + SFRAME_FRE_OFFSET_4B = 2, +}; + +/// Stack frame base register. Bit 0 of sframe_fre_info.fre_info. +enum : uint8_t { SFRAME_BASE_REG_FP = 0, SFRAME_BASE_REG_SP = 1 }; + +LLVM_PACKED_START + +struct sframe_preamble { + uint16_t sfp_magic; + uint8_t sfp_version; + uint8_t sfp_flags; +}; + +struct sframe_header { + sframe_preamble sfh_preamble; + uint8_t sfh_abi_arch; + int8_t sfh_cfa_fixed_fp_offset; + int8_t sfh_cfa_fixed_ra_offset; + uint8_t sfh_auxhdr_len; + uint32_t sfh_num_fdes; + uint32_t sfh_num_fres; + uint32_t sfh_fre_len; + uint32_t sfh_fdeoff; + uint32_t sfh_freoff; +}; + +struct sframe_func_desc_entry { + int32_t sfde_func_start_address; + uint32_t sfde_func_size; + uint32_t sfde_func_start_fre_off; + uint32_t sfde_func_num_fres; + uint8_t sfde_func_info; + uint8_t sfde_func_rep_size; + uint16_t sfde_func_padding2; + + uint8_t getPAuthKey() const { return (sfde_func_info >> 5) & 1; } + uint8_t getFDEType() const { return (sfde_func_info >> 4) & 1; } + uint8_t getFREType() const { return sfde_func_info & 0xf; } + void setPAuthKey(uint8_t P) { setFuncInfo(P, getFDEType(), getFREType()); } + void setFDEType(uint8_t D) { setFuncInfo(getPAuthKey(), D, getFREType()); } + void setFREType(uint8_t R) { setFuncInfo(getPAuthKey(), getFDEType(), R); } + void setFuncInfo(uint8_t PAuthKey, uint8_t FDEType, uint8_t FREType) { + sfde_func_info = + ((PAuthKey & 1) << 5) | ((FDEType & 1) << 4) | (FREType & 0xf); + } +}; + +struct sframe_fre_info { + uint8_t fre_info; + + bool isReturnAddressSigned() const { return fre_info >> 7; } + uint8_t getOffsetSize() const { return (fre_info >> 5) & 3; } + uint8_t getOffsetCount() const { return (fre_info >> 1) & 0xf; } + uint8_t getBaseRegister() const { return fre_info & 1; } + void setReturnAddressSigned(bool RA) { + setFREInfo(RA, getOffsetSize(), getOffsetCount(), getBaseRegister()); + } + void setOffsetSize(uint8_t Sz) { + setFREInfo(isReturnAddressSigned(), Sz, getOffsetCount(), + getBaseRegister()); + } + void setOffsetCount(uint8_t N) { + setFREInfo(isReturnAddressSigned(), getOffsetSize(), N, getBaseRegister()); + } + void setBaseRegister(uint8_t Reg) { + setFREInfo(isReturnAddressSigned(), getOffsetSize(), getOffsetCount(), Reg); + } + void setFREInfo(bool RA, uint8_t Sz, uint8_t N, uint8_t Reg) { + fre_info = ((RA & 1) << 7) | ((Sz & 3) << 5) | ((N & 0xf) << 1) | (Reg & 1); + } +}; + +struct sframe_frame_row_entry_addr1 { + uint8_t sfre_start_address; + sframe_fre_info sfre_info; +}; + +struct sframe_frame_row_entry_addr2 { + uint16_t sfre_start_address; + sframe_fre_info sfre_info; +}; + +struct sframe_frame_row_entry_addr4 { + uint32_t sfre_start_address; + sframe_fre_info sfre_info; +}; + +LLVM_PACKED_END + +} // namespace sframe +} // namespace llvm + +#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..3c8d843d50389 --- /dev/null +++ b/llvm/unittests/BinaryFormat/SFrameTest.cpp @@ -0,0 +1,98 @@ +//===- 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" + +using namespace llvm; +using namespace llvm::sframe; + +namespace { +// Test structure sizes and triviality. +static_assert(std::is_trivial_v); +static_assert(sizeof(sframe_preamble) == 4); + +static_assert(std::is_trivial_v); +static_assert(sizeof(sframe_header) == 28); + +static_assert(std::is_trivial_v); +static_assert(sizeof(sframe_func_desc_entry) == 20); + +static_assert(std::is_trivial_v); +static_assert(sizeof(sframe_frame_row_entry_addr1) == 2); + +static_assert(std::is_trivial_v); +static_assert(sizeof(sframe_frame_row_entry_addr2) == 3); + +static_assert(std::is_trivial_v); +static_assert(sizeof(sframe_frame_row_entry_addr4) == 5); + +TEST(SFrameTest, FDEFlags) { + sframe_func_desc_entry FDE = {}; + EXPECT_EQ(FDE.sfde_func_info, 0u); + EXPECT_EQ(FDE.getPAuthKey(), SFRAME_AARCH64_PAUTH_KEY_A); + EXPECT_EQ(FDE.getFDEType(), SFRAME_FDE_TYPE_PCINC); + EXPECT_EQ(FDE.getFREType(), SFRAME_FRE_TYPE_ADDR1); + + FDE.setPAuthKey(SFRAME_AARCH64_PAUTH_KEY_B); + EXPECT_EQ(FDE.sfde_func_info, 0x20u); + EXPECT_EQ(FDE.getPAuthKey(), SFRAME_AARCH64_PAUTH_KEY_B); + EXPECT_EQ(FDE.getFDEType(), SFRAME_FDE_TYPE_PCINC); + EXPECT_EQ(FDE.getFREType(), SFRAME_FRE_TYPE_ADDR1); + + FDE.setFDEType(SFRAME_FDE_TYPE_PCMASK); + EXPECT_EQ(FDE.sfde_func_info, 0x30u); + EXPECT_EQ(FDE.getPAuthKey(), SFRAME_AARCH64_PAUTH_KEY_B); + EXPECT_EQ(FDE.getFDEType(), SFRAME_FDE_TYPE_PCMASK); + EXPECT_EQ(FDE.getFREType(), SFRAME_FRE_TYPE_ADDR1); + + FDE.setFREType(SFRAME_FRE_TYPE_ADDR4); + EXPECT_EQ(FDE.sfde_func_info, 0x32u); + EXPECT_EQ(FDE.getPAuthKey(), SFRAME_AARCH64_PAUTH_KEY_B); + EXPECT_EQ(FDE.getFDEType(), SFRAME_FDE_TYPE_PCMASK); + EXPECT_EQ(FDE.getFREType(), SFRAME_FRE_TYPE_ADDR4); +} + +TEST(SFrameTest, FREFlags) { + sframe_fre_info Info = {}; + EXPECT_EQ(Info.fre_info, 0u); + EXPECT_FALSE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_1B); + EXPECT_EQ(Info.getOffsetCount(), 0u); + EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_FP); + + Info.setReturnAddressSigned(true); + EXPECT_EQ(Info.fre_info, 0x80u); + EXPECT_TRUE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_1B); + EXPECT_EQ(Info.getOffsetCount(), 0u); + EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_FP); + + Info.setOffsetSize(SFRAME_FRE_OFFSET_4B); + EXPECT_EQ(Info.fre_info, 0xc0u); + EXPECT_TRUE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_4B); + EXPECT_EQ(Info.getOffsetCount(), 0u); + EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_FP); + + Info.setOffsetCount(3); + EXPECT_EQ(Info.fre_info, 0xc6u); + EXPECT_TRUE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_4B); + EXPECT_EQ(Info.getOffsetCount(), 3u); + EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_FP); + + Info.setBaseRegister(SFRAME_BASE_REG_SP); + EXPECT_EQ(Info.fre_info, 0xc7u); + EXPECT_TRUE(Info.isReturnAddressSigned()); + EXPECT_EQ(Info.getOffsetSize(), SFRAME_FRE_OFFSET_4B); + EXPECT_EQ(Info.getOffsetCount(), 3u); + EXPECT_EQ(Info.getBaseRegister(), SFRAME_BASE_REG_SP); +} + +} // namespace From ae9b04047cfd68a28e5a99cffce1341240219c4a Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Fri, 4 Jul 2025 16:04:43 +0200 Subject: [PATCH 2/2] [Object] Beginnings of SFrame parser and dumper This PR adds the SFrameParser class and uses it from llvm-readobj to dump the section contents. Currently, it only supports parsing the SFrame section header. Other parts of the section will be added in follow-up patches. llvm-readobj uses the same sframe flag syntax as GNU readelf, but I have not attempted match the output format of the tool. I'm starting with the "llvm" output format because it's easier to generate and lets us tweak the format to make it useful for testing the generation code. If needed, support for the GNU format could be added by overriding this functionality in the GNU ELF Dumper. --- llvm/include/llvm/BinaryFormat/SFrame.h | 20 ++- .../llvm/BinaryFormat/SFrameConstants.def | 38 ++++++ llvm/include/llvm/Object/SFrameParser.h | 47 +++++++ llvm/lib/BinaryFormat/CMakeLists.txt | 1 + llvm/lib/BinaryFormat/SFrame.cpp | 40 ++++++ llvm/lib/Object/CMakeLists.txt | 1 + llvm/lib/Object/SFrameParser.cpp | 65 +++++++++ .../tools/llvm-readobj/ELF/sframe-header.test | 124 ++++++++++++++++++ llvm/tools/llvm-readobj/ObjDumper.cpp | 51 +++++++ llvm/tools/llvm-readobj/ObjDumper.h | 2 + llvm/tools/llvm-readobj/Opts.td | 2 + llvm/tools/llvm-readobj/llvm-readobj.cpp | 4 + 12 files changed, 388 insertions(+), 7 deletions(-) create mode 100644 llvm/include/llvm/BinaryFormat/SFrameConstants.def create mode 100644 llvm/include/llvm/Object/SFrameParser.h create mode 100644 llvm/lib/BinaryFormat/SFrame.cpp create mode 100644 llvm/lib/Object/SFrameParser.cpp create mode 100644 llvm/test/tools/llvm-readobj/ELF/sframe-header.test diff --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h index c73553756bc23..f58dad140cc68 100644 --- a/llvm/include/llvm/BinaryFormat/SFrame.h +++ b/llvm/include/llvm/BinaryFormat/SFrame.h @@ -18,31 +18,33 @@ #ifndef LLVM_BINARYFORMAT_SFRAME_H #define LLVM_BINARYFORMAT_SFRAME_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DataTypes.h" namespace llvm { +template struct EnumEntry; + namespace sframe { constexpr uint16_t SFRAME_MAGIC = 0xDEE2; enum : uint8_t { - SFRAME_VERSION_1 = 1, - SFRAME_VERSION_2 = 2, +#define HANDLE_SFRAME_VERSION(CODE, NAME) SFRAME_##NAME = CODE, +#include "llvm/BinaryFormat/SFrameConstants.def" }; /// sframe_preable.sfp_flags flags. enum : uint8_t { - SFRAME_F_FDE_SORTED = 0x1, - SFRAME_F_FRAME_POINTER = 0x2, +#define HANDLE_SFRAME_HEADER_FLAG(CODE, NAME) SFRAME_F_##NAME = CODE, +#include "llvm/BinaryFormat/SFrameConstants.def" }; /// Possible values for sframe_header.sfh_abi_arch. enum : uint8_t { - SFRAME_ABI_AARCH64_ENDIAN_BIG = 1, - SFRAME_ABI_AARCH64_ENDIAN_LITTLE = 2, - SFRAME_ABI_AMD64_ENDIAN_LITTLE = 3 +#define HANDLE_SFRAME_ABI(CODE, NAME) SFRAME_ABI_##NAME = CODE, +#include "llvm/BinaryFormat/SFrameConstants.def" }; /// SFrame FRE Types. Bits 0-3 of sframe_func_desc_entry.sfde_func_info. @@ -159,6 +161,10 @@ struct sframe_frame_row_entry_addr4 { LLVM_PACKED_END +ArrayRef> getVersions(); +ArrayRef> getHeaderFlags(); +ArrayRef> getABIs(); + } // namespace sframe } // namespace llvm diff --git a/llvm/include/llvm/BinaryFormat/SFrameConstants.def b/llvm/include/llvm/BinaryFormat/SFrameConstants.def new file mode 100644 index 0000000000000..d6c131330e2c0 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/SFrameConstants.def @@ -0,0 +1,38 @@ +//===- SFrameConstants.def --------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#if !(defined(HANDLE_SFRAME_VERSION) || defined(HANDLE_SFRAME_HEADER_FLAG) || \ + defined(HANDLE_SFRAME_ABI)) +#error "Missing HANDLE_SFRAME definition" +#endif + +#ifndef HANDLE_SFRAME_VERSION +#define HANDLE_SFRAME_VERSION(CODE, NAME) +#endif + +#ifndef HANDLE_SFRAME_HEADER_FLAG +#define HANDLE_SFRAME_HEADER_FLAG(CODE, NAME) +#endif + +#ifndef HANDLE_SFRAME_ABI +#define HANDLE_SFRAME_ABI(CODE, NAME) +#endif + +HANDLE_SFRAME_VERSION(0x01, VERSION_1) +HANDLE_SFRAME_VERSION(0x02, VERSION_2) + +HANDLE_SFRAME_HEADER_FLAG(0x01, FDE_SORTED) +HANDLE_SFRAME_HEADER_FLAG(0x02, FRAME_POINTER) + +HANDLE_SFRAME_ABI(0x01, AARCH64_ENDIAN_BIG) +HANDLE_SFRAME_ABI(0x02, AARCH64_ENDIAN_LITTLE) +HANDLE_SFRAME_ABI(0x03, AMD64_ENDIAN_LITTLE) + +#undef HANDLE_SFRAME_VERSION +#undef HANDLE_SFRAME_HEADER_FLAG +#undef HANDLE_SFRAME_ABI diff --git a/llvm/include/llvm/Object/SFrameParser.h b/llvm/include/llvm/Object/SFrameParser.h new file mode 100644 index 0000000000000..c297650d82558 --- /dev/null +++ b/llvm/include/llvm/Object/SFrameParser.h @@ -0,0 +1,47 @@ +//===- SFrameParser.h -------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_OBJECT_SFRAME_H +#define LLVM_OBJECT_SFRAME_H + +#include "llvm/BinaryFormat/SFrame.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +namespace object { + +class SFrameParser { +public: + static Expected create(ArrayRef Contents); + + const sframe::sframe_preamble &getPreamble() const { + return Header.sfh_preamble; + } + const sframe::sframe_header &getHeader() const { return Header; } + + bool usesFixedRAOffset() const { + return Header.sfh_abi_arch == sframe::SFRAME_ABI_AMD64_ENDIAN_LITTLE; + } + bool usesFixedFPOffset() const { + return false; // Not used in any currently defined ABI. + } + +private: + DataExtractor Data; + sframe::sframe_header Header; + + SFrameParser(DataExtractor Data, sframe::sframe_header Header) + : Data(Data), Header(Header) {} +}; + +} // end namespace object +} // end namespace llvm + +#endif // LLVM_OBJECT_SFRAME_H diff --git a/llvm/lib/BinaryFormat/CMakeLists.txt b/llvm/lib/BinaryFormat/CMakeLists.txt index 38ba2d9e85a06..4b2debb7ae236 100644 --- a/llvm/lib/BinaryFormat/CMakeLists.txt +++ b/llvm/lib/BinaryFormat/CMakeLists.txt @@ -11,6 +11,7 @@ add_llvm_component_library(LLVMBinaryFormat MsgPackDocumentYAML.cpp MsgPackReader.cpp MsgPackWriter.cpp + SFrame.cpp Wasm.cpp XCOFF.cpp diff --git a/llvm/lib/BinaryFormat/SFrame.cpp b/llvm/lib/BinaryFormat/SFrame.cpp new file mode 100644 index 0000000000000..439b303bbb9e8 --- /dev/null +++ b/llvm/lib/BinaryFormat/SFrame.cpp @@ -0,0 +1,40 @@ +//===-- SFrame.cpp -----------------------------------------------*- C++-*-===// +// +// 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 "llvm/Support/ScopedPrinter.h" + +using namespace llvm; + +ArrayRef> sframe::getVersions() { + static constexpr EnumEntry Versions[] = { +#define HANDLE_SFRAME_VERSION(CODE, NAME) \ + {"SFRAME_" #NAME, sframe::SFRAME_##NAME}, +#include "llvm/BinaryFormat/SFrameConstants.def" + }; + + return ArrayRef(Versions); +} + +ArrayRef> sframe::getHeaderFlags() { + static constexpr EnumEntry Flags[] = { +#define HANDLE_SFRAME_HEADER_FLAG(CODE, NAME) \ + {"SFRAME_F_" #NAME, sframe::SFRAME_F_##NAME}, +#include "llvm/BinaryFormat/SFrameConstants.def" + }; + return ArrayRef(Flags); +} + +ArrayRef> sframe::getABIs() { + static constexpr EnumEntry ABIs[] = { +#define HANDLE_SFRAME_ABI(CODE, NAME) \ + {"SFRAME_ABI_" #NAME, sframe::SFRAME_ABI_##NAME}, +#include "llvm/BinaryFormat/SFrameConstants.def" + }; + return ArrayRef(ABIs); +} diff --git a/llvm/lib/Object/CMakeLists.txt b/llvm/lib/Object/CMakeLists.txt index 870169a83174f..0f6d2f7c59a5c 100644 --- a/llvm/lib/Object/CMakeLists.txt +++ b/llvm/lib/Object/CMakeLists.txt @@ -25,6 +25,7 @@ add_llvm_component_library(LLVMObject OffloadBundle.cpp RecordStreamer.cpp RelocationResolver.cpp + SFrameParser.cpp SymbolicFile.cpp SymbolSize.cpp TapiFile.cpp diff --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp new file mode 100644 index 0000000000000..93d61a0e41478 --- /dev/null +++ b/llvm/lib/Object/SFrameParser.cpp @@ -0,0 +1,65 @@ +//===- SFrameParser.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/Object/SFrameParser.h" +#include "llvm/Object/Error.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::sframe; + +Expected SFrameParser::create(ArrayRef Contents) { + sframe_header Header; + sframe_preamble &Preamble = Header.sfh_preamble; + + // IsLittleEndian will be swapped if needed, AddressSize is unused. + DataExtractor Data(Contents, /*IsLittleEndian=*/true, /*AddressSize=*/0); + DataExtractor::Cursor Cursor(0); + Preamble.sfp_magic = Data.getU16(Cursor); + if (!Cursor) + return Cursor.takeError(); + + switch (Preamble.sfp_magic) { + case SFRAME_MAGIC: + break; + case byteswap(SFRAME_MAGIC): + // Fix endianness. + Preamble.sfp_magic = SFRAME_MAGIC; + // And make sure everything that comes afterwards is read correctly. + Data = DataExtractor(Data.getData(), !Data.isLittleEndian(), + Data.getAddressSize()); + break; + default: + return make_error("invalid magic number", + object_error::parse_failed); + } + Preamble.sfp_version = Data.getU8(Cursor); + Preamble.sfp_flags = Data.getU8(Cursor); + if (!Cursor) + return Cursor.takeError(); + + if (Preamble.sfp_version != SFRAME_VERSION_2) { + return make_error("invalid/unsupported version number", + object_error::parse_failed); + } + + Header.sfh_abi_arch = Data.getU8(Cursor); + Header.sfh_cfa_fixed_fp_offset = (int8_t)Data.getU8(Cursor); + Header.sfh_cfa_fixed_ra_offset = (int8_t)Data.getU8(Cursor); + Header.sfh_auxhdr_len = Data.getU8(Cursor); + Header.sfh_num_fdes = Data.getU32(Cursor); + Header.sfh_num_fres = Data.getU32(Cursor); + Header.sfh_fre_len = Data.getU32(Cursor); + Header.sfh_fdeoff = Data.getU32(Cursor); + Header.sfh_freoff = Data.getU32(Cursor); + + if (!Cursor) + return Cursor.takeError(); + + return SFrameParser(Data, Header); +} diff --git a/llvm/test/tools/llvm-readobj/ELF/sframe-header.test b/llvm/test/tools/llvm-readobj/ELF/sframe-header.test new file mode 100644 index 0000000000000..6731d7d154a49 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/sframe-header.test @@ -0,0 +1,124 @@ +## Check parsing and dumping of the SFrame header. +# RUN: yaml2obj --docnum=1 %s -o %t.1 +# RUN: llvm-readobj --sframe=.sframe_1b --sframe=.sframe_bad_magic \ +# RUN: --sframe=.sframe_3b --sframe=.sframe_bad_version --sframe=.sframe_6b \ +# RUN: --sframe=.sframe_header %t.1 2>&1 | FileCheck %s --check-prefix=CASE1 + +## Check big-endian support and the handling of --sframe argument default. +# RUN: yaml2obj --docnum=2 %s -o %t.2 +# RUN: llvm-readobj --sframe %t.2 2>&1 | FileCheck %s --check-prefix=CASE2 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .sframe_1b + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + ContentArray: [ 0x00 ] +# CASE1-LABEL: Contents of SFrame section '.sframe_1b': +# CASE1: warning: {{.*}}: unexpected end of data at offset 0x1 while reading [0x0, 0x2) + + - Name: .sframe_bad_magic + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + ContentArray: [ 0xde, 0xad ] +# CASE1-LABEL: Contents of SFrame section '.sframe_bad_magic': +# CASE1: warning: {{.*}}: invalid magic number + + - Name: .sframe_3b + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + ContentArray: [ 0xe2, 0xde, 0x01 ] +# CASE1-LABEL: Contents of SFrame section '.sframe_3b': +# CASE1: warning: {{.*}}: unexpected end of data at offset 0x3 while reading [0x3, 0x4) + + - Name: .sframe_bad_version + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xe2, 0xde, 0x01, 0x00 # Preamble (magic, version, flags) + ] +# CASE1-LABEL: Contents of SFrame section '.sframe_bad_version': +# CASE1: warning: {{.*}}: invalid/unsupported version number + + - Name: .sframe_6b + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xe2, 0xde, 0x02, 0x00, # Preamble (magic, version, flags) + 0x01, 0x02 + ] + +# CASE1-LABEL: Contents of SFrame section '.sframe_6b': +# CASE1: warning: {{.*}}: unexpected end of data at offset 0x6 while reading [0x6, 0x7) + + - Name: .sframe_header + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xe2, 0xde, 0x02, 0x00, # Preamble (magic, version, flags) + # Header: + 0x03, 0x42, 0x47, 0x00, # ABI, Fixed FP offset, Fixed RA Offset, AUX header length + 0x01, 0x00, 0x00, 0x00, # Number of FDEs + 0x10, 0x00, 0x00, 0x00, # Number of FREs + 0x00, 0x10, 0x00, 0x00, # FRE length + 0x04, 0x00, 0x00, 0x00, # FDE offset + 0x00, 0x01, 0x00, 0x00, # FRE offset + ] +# CASE1-LABEL: Contents of SFrame section '.sframe_header': +# CASE1: Header { +# CASE1-NEXT: Magic: 0xDEE2 +# CASE1-NEXT: Version: SFRAME_VERSION_2 (0x2) +# CASE1-NEXT: Flags [ (0x0) +# CASE1-NEXT: ] +# CASE1-NEXT: ABI: SFRAME_ABI_AMD64_ENDIAN_LITTLE (0x3) +# CASE1-NEXT: CFA fixed RA offset: 71 +# CASE1-NEXT: CFA fixed FP offset (unused): 66 +# CASE1-NEXT: Auxiliary header length: 0 +# CASE1-NEXT: Num FDEs: 1 +# CASE1-NEXT: Num FREs: 16 +# CASE1-NEXT: FRE subsection length: 4096 +# CASE1-NEXT: FDE subsection offset: 4 +# CASE1-NEXT: FRE subsection offset: 256 +# CASE1-NEXT: } + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2MSB + Type: ET_EXEC + Machine: EM_AARCH64 +Sections: + - Name: .sframe + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xde, 0xe2, 0x02, 0x00, # Preamble (magic, version, flags) + # Header: + 0x01, 0x42, 0x47, 0x00, # ABI, Fixed FP offset, Fixed RA Offset, AUX header length + 0x00, 0x00, 0x00, 0x01, # Number of FDEs + 0x00, 0x00, 0x00, 0x10, # Number of FREs + 0x00, 0x00, 0x10, 0x00, # FRE length + 0x00, 0x00, 0x00, 0x04, # FDE offset + 0x00, 0x00, 0x01, 0x00, # FRE offset + ] +# CASE2-LABEL: Contents of SFrame section '.sframe': +# CASE2: Header { +# CASE2-NEXT: Magic: 0xDEE2 +# CASE2-NEXT: Version: SFRAME_VERSION_2 (0x2) +# CASE2-NEXT: Flags [ (0x0) +# CASE2-NEXT: ] +# CASE2-NEXT: ABI: SFRAME_ABI_AARCH64_ENDIAN_BIG (0x1) +# CASE2-NEXT: CFA fixed RA offset (unused): 71 +# CASE2-NEXT: CFA fixed FP offset (unused): 66 +# CASE2-NEXT: Auxiliary header length: 0 +# CASE2-NEXT: Num FDEs: 1 +# CASE2-NEXT: Num FREs: 16 +# CASE2-NEXT: FRE subsection length: 4096 +# CASE2-NEXT: FDE subsection offset: 4 +# CASE2-NEXT: FRE subsection offset: 256 +# CASE2-NEXT: } diff --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp index d3c613ee823ba..00317ba9df6c7 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.cpp +++ b/llvm/tools/llvm-readobj/ObjDumper.cpp @@ -13,9 +13,11 @@ #include "ObjDumper.h" #include "llvm-readobj.h" +#include "llvm/BinaryFormat/SFrame.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Decompressor.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/SFrameParser.h" #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ScopedPrinter.h" @@ -230,4 +232,53 @@ void ObjDumper::printSectionsAsHex(const object::ObjectFile &Obj, } } +void ObjDumper::printSectionsAsSFrame(const object::ObjectFile &Obj, + ArrayRef Sections) { + for (object::SectionRef Section : + getSectionRefsByNameOrIndex(Obj, Sections)) { + StringRef SectionName = unwrapOrError(Obj.getFileName(), Section.getName()); + W.getOStream() << '\n'; + W.startLine() << "Contents of SFrame section '" << SectionName << "':\n"; + + StringRef SectionContent = + unwrapOrError(Obj.getFileName(), Section.getContents()); + Expected Parser = + object::SFrameParser::create(arrayRefFromStringRef(SectionContent)); + if (!Parser) { + reportWarning(Parser.takeError(), Obj.getFileName()); + continue; + } + + W.indent(); + W.objectBegin("Header"); + + const sframe::sframe_preamble &Preamble = Parser->getPreamble(); + W.printHex("Magic", Preamble.sfp_magic); + W.printEnum("Version", Preamble.sfp_version, sframe::getVersions()); + W.printFlags("Flags", Preamble.sfp_flags, sframe::getHeaderFlags()); + + const sframe::sframe_header &Header = Parser->getHeader(); + W.printEnum("ABI", Header.sfh_abi_arch, sframe::getABIs()); + + W.printNumber(("CFA fixed RA offset" + + Twine(Parser->usesFixedRAOffset() ? "" : " (unused)")) + .str(), + Header.sfh_cfa_fixed_ra_offset); + + W.printNumber(("CFA fixed FP offset" + + Twine(Parser->usesFixedFPOffset() ? "" : " (unused)")) + .str(), + Header.sfh_cfa_fixed_fp_offset); + + W.printNumber("Auxiliary header length", Header.sfh_auxhdr_len); + W.printNumber("Num FDEs", Header.sfh_num_fdes); + W.printNumber("Num FREs", Header.sfh_num_fres); + W.printNumber("FRE subsection length", Header.sfh_fre_len); + W.printNumber("FDE subsection offset", Header.sfh_fdeoff); + W.printNumber("FRE subsection offset", Header.sfh_freoff); + W.objectEnd(); + W.unindent(); + } +} + } // namespace llvm diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index a76afbe9c88c7..d76340eb6aa53 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -182,6 +182,8 @@ class ObjDumper { ArrayRef Sections, bool Decompress); void printSectionsAsHex(const object::ObjectFile &Obj, ArrayRef Sections, bool Decompress); + void printSectionsAsSFrame(const object::ObjectFile &Obj, + ArrayRef Sections); std::function WarningHandler; void reportUniqueWarning(Error Err) const; diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td index f95461aaca1a7..48d43cc635a4f 100644 --- a/llvm/tools/llvm-readobj/Opts.td +++ b/llvm/tools/llvm-readobj/Opts.td @@ -62,6 +62,8 @@ def memtag : FF<"memtag", "Display memory tagging metadata (modes, Android notes def needed_libs : FF<"needed-libs", "Display the needed libraries">, Group; def notes : FF<"notes", "Display notes">, Group; def program_headers : FF<"program-headers", "Display program headers">, Group; +def sframe_EQ : Joined<["--"], "sframe=">, HelpText<"Display SFrame section ">, MetaVarName<"">, Group; +def sframe: FF<"sframe", "Alias for --sframe=.sframe">, Alias, AliasArgs<[".sframe"]>, Group; def version_info : FF<"version-info", "Display version sections">, Group; // Mach-O specific options. diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 1231c02035d1f..4bf8f3a99c558 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -137,6 +137,7 @@ static bool NeededLibraries; static bool Notes; static bool ProgramHeaders; static bool SectionGroups; +static std::vector SFrame; static bool VersionInfo; // Mach-O specific options. @@ -275,6 +276,7 @@ static void parseOptions(const opt::InputArgList &Args) { opts::PrettyPrint = Args.hasArg(OPT_pretty_print); opts::ProgramHeaders = Args.hasArg(OPT_program_headers); opts::SectionGroups = Args.hasArg(OPT_section_groups); + opts::SFrame = Args.getAllArgValues(OPT_sframe_EQ); if (Arg *A = Args.getLastArg(OPT_sort_symbols_EQ)) { for (StringRef KeyStr : llvm::split(A->getValue(), ",")) { SortSymbolKeyTy KeyType = StringSwitch(KeyStr) @@ -539,6 +541,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, Dumper->printStackMap(); if (opts::PrintStackSizes) Dumper->printStackSizes(); + if (!opts::SFrame.empty()) + Dumper->printSectionsAsSFrame(Obj, opts::SFrame); } /// Dumps each object file in \a Arc;