diff --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h new file mode 100644 index 0000000000000..f58dad140cc68 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/SFrame.h @@ -0,0 +1,171 @@ +//===-- 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/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 { +#define HANDLE_SFRAME_VERSION(CODE, NAME) SFRAME_##NAME = CODE, +#include "llvm/BinaryFormat/SFrameConstants.def" +}; + +/// sframe_preable.sfp_flags flags. +enum : uint8_t { +#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 { +#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. +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 + +ArrayRef> getVersions(); +ArrayRef> getHeaderFlags(); +ArrayRef> getABIs(); + +} // namespace sframe +} // namespace llvm + +#endif // LLVM_BINARYFORMAT_SFRAME_H 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; 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