From aa7ada1dfbe21a0c83474eb3de54e08eb607f8b8 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Mon, 21 Jul 2025 08:46:21 +0200 Subject: [PATCH] [Object] Beginnings of SFrame parser and dumper (#147294) 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. For more information, see the [sframe specification](https://sourceware.org/binutils/wiki/sframe) and the related [RFC](https://discourse.llvm.org/t/rfc-adding-sframe-support-to-llvm/86900). --- llvm/include/llvm/BinaryFormat/SFrame.h | 28 ++-- .../llvm/BinaryFormat/SFrameConstants.def | 39 +++++ llvm/include/llvm/Object/SFrameParser.h | 48 ++++++ llvm/lib/BinaryFormat/CMakeLists.txt | 1 + llvm/lib/BinaryFormat/SFrame.cpp | 37 +++++ llvm/lib/Object/CMakeLists.txt | 1 + llvm/lib/Object/SFrameParser.cpp | 55 +++++++ .../tools/llvm-readobj/ELF/sframe-header.test | 148 ++++++++++++++++++ llvm/tools/llvm-readobj/ELFDumper.cpp | 59 +++++++ llvm/tools/llvm-readobj/ObjDumper.cpp | 6 +- llvm/tools/llvm-readobj/ObjDumper.h | 5 + llvm/tools/llvm-readobj/Opts.td | 2 + llvm/tools/llvm-readobj/llvm-readobj.cpp | 4 + 13 files changed, 420 insertions(+), 13 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 16d3b16c6c2d..98dbe38fb2bc 100644 --- a/llvm/include/llvm/BinaryFormat/SFrame.h +++ b/llvm/include/llvm/BinaryFormat/SFrame.h @@ -15,33 +15,36 @@ #ifndef LLVM_BINARYFORMAT_SFRAME_H #define LLVM_BINARYFORMAT_SFRAME_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Endian.h" -namespace llvm::sframe { +namespace llvm { + +template struct EnumEntry; + +namespace sframe { LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); constexpr uint16_t Magic = 0xdee2; enum class Version : uint8_t { - V1 = 1, - V2 = 2, +#define HANDLE_SFRAME_VERSION(CODE, NAME) NAME = CODE, +#include "llvm/BinaryFormat/SFrameConstants.def" }; enum class Flags : uint8_t { - FDESorted = 0x01, - FramePointer = 0x02, - FDEFuncStartPCRel = 0x04, +#define HANDLE_SFRAME_FLAG(CODE, NAME) NAME = CODE, +#include "llvm/BinaryFormat/SFrameConstants.def" V2AllFlags = FDESorted | FramePointer | FDEFuncStartPCRel, LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/0xff), }; enum class ABI : uint8_t { - AArch64EndianBig = 1, - AArch64EndianLittle = 2, - AMD64EndianLittle = 3, +#define HANDLE_SFRAME_ABI(CODE, NAME) NAME = CODE, +#include "llvm/BinaryFormat/SFrameConstants.def" }; /// SFrame FRE Types. Bits 0-3 of FuncDescEntry.Info. @@ -160,6 +163,11 @@ template using FrameRowEntryAddr1 = FrameRowEntry; template using FrameRowEntryAddr2 = FrameRowEntry; template using FrameRowEntryAddr4 = FrameRowEntry; -} // namespace llvm::sframe +ArrayRef> getVersions(); +ArrayRef> getFlags(); +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 000000000000..643b15f438c8 --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/SFrameConstants.def @@ -0,0 +1,39 @@ +//===- 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_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_FLAG +#define HANDLE_SFRAME_FLAG(CODE, NAME) +#endif + +#ifndef HANDLE_SFRAME_ABI +#define HANDLE_SFRAME_ABI(CODE, NAME) +#endif + +HANDLE_SFRAME_VERSION(0x01, V1) +HANDLE_SFRAME_VERSION(0x02, V2) + +HANDLE_SFRAME_FLAG(0x01, FDESorted) +HANDLE_SFRAME_FLAG(0x02, FramePointer) +HANDLE_SFRAME_FLAG(0x04, FDEFuncStartPCRel) + +HANDLE_SFRAME_ABI(0x01, AArch64EndianBig) +HANDLE_SFRAME_ABI(0x02, AArch64EndianLittle) +HANDLE_SFRAME_ABI(0x03, AMD64EndianLittle) + +#undef HANDLE_SFRAME_VERSION +#undef HANDLE_SFRAME_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 000000000000..cf4fe20e8443 --- /dev/null +++ b/llvm/include/llvm/Object/SFrameParser.h @@ -0,0 +1,48 @@ +//===- 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/ADT/ArrayRef.h" +#include "llvm/BinaryFormat/SFrame.h" +#include "llvm/Support/Error.h" +#include + +namespace llvm { +namespace object { + +template class SFrameParser { +public: + static Expected create(ArrayRef Contents); + + const sframe::Preamble &getPreamble() const { return Header.Preamble; } + const sframe::Header &getHeader() const { return Header; } + + bool usesFixedRAOffset() const { + return getHeader().ABIArch == sframe::ABI::AMD64EndianLittle; + } + bool usesFixedFPOffset() const { + return false; // Not used in any currently defined ABI. + } + +private: + ArrayRef Data; + const sframe::Header &Header; + + SFrameParser(ArrayRef Data, const sframe::Header &Header) + : Data(Data), Header(Header) {} +}; + +extern template class SFrameParser; +extern template class SFrameParser; + +} // 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 38ba2d9e85a0..4b2debb7ae23 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 000000000000..3b436afd3208 --- /dev/null +++ b/llvm/lib/BinaryFormat/SFrame.cpp @@ -0,0 +1,37 @@ +//===-- 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) {#NAME, sframe::Version::NAME}, +#include "llvm/BinaryFormat/SFrameConstants.def" + }; + + return ArrayRef(Versions); +} + +ArrayRef> sframe::getFlags() { + static constexpr EnumEntry Flags[] = { +#define HANDLE_SFRAME_FLAG(CODE, NAME) {#NAME, sframe::Flags::NAME}, +#include "llvm/BinaryFormat/SFrameConstants.def" + }; + return ArrayRef(Flags); +} + +ArrayRef> sframe::getABIs() { + static constexpr EnumEntry ABIs[] = { +#define HANDLE_SFRAME_ABI(CODE, NAME) {#NAME, 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 870169a83174..0f6d2f7c59a5 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 000000000000..2d74d1d6b382 --- /dev/null +++ b/llvm/lib/Object/SFrameParser.cpp @@ -0,0 +1,55 @@ +//===- 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/BinaryFormat/SFrame.h" +#include "llvm/Object/Error.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; +using namespace llvm::object; + +template +static Expected getDataSliceAs(ArrayRef Data, + uint64_t Offset) { + static_assert(std::is_trivial_v); + if (Data.size() < Offset + sizeof(T)) { + return createStringError( + formatv("unexpected end of data at offset {0:x} while reading [{1:x}, " + "{2:x})", + Data.size(), Offset, Offset + sizeof(T)) + .str(), + object_error::unexpected_eof); + } + return *reinterpret_cast(Data.data() + Offset); +} + +template +Expected> SFrameParser::create(ArrayRef Contents) { + Expected &> Preamble = + getDataSliceAs>(Contents, 0); + if (!Preamble) + return Preamble.takeError(); + + if (Preamble->Magic != sframe::Magic) + return createError( + formatv("invalid magic number ({0:x+4})", Preamble->Magic.value())); + if (Preamble->Version != sframe::Version::V2) + return createError( + formatv("invalid/unsupported version number ({0})", + static_cast(Preamble->Version.value()))); + + Expected &> Header = + getDataSliceAs>(Contents, 0); + if (!Header) + return Header.takeError(); + return SFrameParser(Contents, *Header); +} + +template class llvm::object::SFrameParser; +template class llvm::object::SFrameParser; 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 000000000000..f827296b1c39 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/sframe-header.test @@ -0,0 +1,148 @@ +## Check parsing and dumping of the SFrame header. +# RUN: yaml2obj --docnum=1 %s -o %t.1 +# RUN: llvm-readobj --sframe=.sframe_bad_sh_size --sframe=.sframe_1b \ +# RUN: --sframe=.sframe_bad_magic --sframe=.sframe_bad_version \ +# RUN: --sframe=.sframe_6b --sframe=.sframe_header %t.1 2>&1 | \ +# RUN: FileCheck %s --strict-whitespace --match-full-lines \ +# RUN: -DFILE=%t.1 --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 | \ +# RUN: FileCheck %s --strict-whitespace --match-full-lines \ +# RUN: -DFILE=%t.2 --check-prefix=CASE2 + +## Check handling of corrupted elf files (bad sh_name) +# RUN: yaml2obj --docnum=3 %s -o %t.3 +# RUN: not llvm-readobj --sframe %t.3 2>&1 | \ +# RUN: FileCheck %s --strict-whitespace --match-full-lines \ +# RUN: -DFILE=%t.3 --check-prefix=CASE3 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .sframe_bad_sh_size + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ShSize: 0xfffff +# CASE1-LABEL:SFrame section '.sframe_bad_sh_size' { +# CASE1:{{.*}}: warning: '[[FILE]]': The end of the file was unexpectedly encountered + - Name: .sframe_1b + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ContentArray: [ 0x00 ] +# CASE1-LABEL:SFrame section '.sframe_1b' { +# CASE1:{{.*}}: warning: '[[FILE]]': invalid sframe section: unexpected end of data at offset 0x1 while reading [0x0, 0x4) + + - Name: .sframe_bad_magic + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ContentArray: [ 0xde, 0xad, 0xbe, 0xef] +# CASE1-LABEL:SFrame section '.sframe_bad_magic' { +# CASE1:{{.*}}: warning: '[[FILE]]': invalid sframe section: invalid magic number (0xadde) + + - Name: .sframe_bad_version + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xe2, 0xde, 0x01, 0x00 # Preamble (magic, version, flags) + ] +# CASE1-LABEL:SFrame section '.sframe_bad_version' { +# CASE1:{{.*}}: warning: '[[FILE]]': invalid sframe section: invalid/unsupported version number (1) + + - Name: .sframe_6b + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xe2, 0xde, 0x02, 0x00, # Preamble (magic, version, flags) + 0x01, 0x02 + ] + +# CASE1-LABEL:SFrame section '.sframe_6b' { +# CASE1:{{.*}}: warning: '[[FILE]]': invalid sframe section: unexpected end of data at offset 0x6 while reading [0x0, 0x1c) + + - Name: .sframe_header + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xe2, 0xde, 0x02, 0x06, # 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:SFrame section '.sframe_header' { +# CASE1: Header { +# CASE1-NEXT: Magic: 0xDEE2 +# CASE1-NEXT: Version: V2 (0x2) +# CASE1-NEXT: Flags [ (0x6) +# CASE1-NEXT: FDEFuncStartPCRel (0x4){{ *}} +# CASE1-NEXT: FramePointer (0x2){{ *}} +# CASE1-NEXT: ] +# CASE1-NEXT: ABI: AMD64EndianLittle (0x3) +# CASE1-NEXT: CFA fixed FP offset (unused): 66 +# CASE1-NEXT: CFA fixed RA offset: 71 +# 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: } +# CASE1-NEXT:} + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2MSB + Type: ET_EXEC +Sections: + - Name: .sframe + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xde, 0xe2, 0x02, 0x01, # 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:SFrame section '.sframe' { +# CASE2: Header { +# CASE2-NEXT: Magic: 0xDEE2 +# CASE2-NEXT: Version: V2 (0x2) +# CASE2-NEXT: Flags [ (0x1) +# CASE2-NEXT: FDESorted (0x1){{ *}} +# CASE2-NEXT: ] +# CASE2-NEXT: ABI: AArch64EndianBig (0x1) +# CASE2-NEXT: CFA fixed FP offset (unused): 66 +# CASE2-NEXT: CFA fixed RA offset (unused): 71 +# 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: } +# CASE2-NEXT:} + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2MSB + Type: ET_EXEC +Sections: + - Name: .corrupted + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ShName: 0x10000 +# CASE3:{{.*}}: error: '[[FILE]]': a section [index 1] has an invalid sh_name (0x10000) offset which goes past the end of the section name string table diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index ccc64fec1295..2867d48a400d 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -30,6 +30,7 @@ #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/BinaryFormat/MsgPackDocument.h" +#include "llvm/BinaryFormat/SFrame.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" @@ -38,6 +39,7 @@ #include "llvm/Object/Error.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/RelocationResolver.h" +#include "llvm/Object/SFrameParser.h" #include "llvm/Object/StackMapParser.h" #include "llvm/Support/AArch64AttributeParser.h" #include "llvm/Support/AMDGPUMetadata.h" @@ -225,6 +227,8 @@ public: void printArchSpecificInfo() override; void printStackMap() const override; void printMemtag() override; + void printSectionsAsSFrame(ArrayRef Sections) override; + ArrayRef getMemtagGlobalsSectionContents(uint64_t ExpectedAddr); // Hash histogram shows statistics of how efficient the hash was for the @@ -6429,6 +6433,61 @@ template void ELFDumper::printMemtag() { printMemtag(DynamicEntries, AndroidNoteDesc, GlobalDescriptors); } +template +void ELFDumper::printSectionsAsSFrame(ArrayRef Sections) { + constexpr endianness E = ELFT::Endianness; + for (object::SectionRef Section : + getSectionRefsByNameOrIndex(ObjF, Sections)) { + // Validity of sections names checked in getSectionRefsByNameOrIndex. + StringRef SectionName = cantFail(Section.getName()); + + DictScope SectionScope(W, + formatv("SFrame section '{0}'", SectionName).str()); + + StringRef SectionContent; + if (Error Err = Section.getContents().moveInto(SectionContent)) { + reportWarning(std::move(Err), FileName); + continue; + } + + Expected> Parser = + object::SFrameParser::create(arrayRefFromStringRef(SectionContent)); + if (!Parser) { + reportWarning(createError("invalid sframe section: " + + toString(Parser.takeError())), + FileName); + continue; + } + + DictScope HeaderScope(W, "Header"); + + const sframe::Preamble &Preamble = Parser->getPreamble(); + W.printHex("Magic", Preamble.Magic.value()); + W.printEnum("Version", Preamble.Version.value(), sframe::getVersions()); + W.printFlags("Flags", Preamble.Flags.value(), sframe::getFlags()); + + const sframe::Header &Header = Parser->getHeader(); + W.printEnum("ABI", Header.ABIArch.value(), sframe::getABIs()); + + W.printNumber(("CFA fixed FP offset" + + Twine(Parser->usesFixedFPOffset() ? "" : " (unused)")) + .str(), + Header.CFAFixedFPOffset.value()); + + W.printNumber(("CFA fixed RA offset" + + Twine(Parser->usesFixedRAOffset() ? "" : " (unused)")) + .str(), + Header.CFAFixedRAOffset.value()); + + W.printNumber("Auxiliary header length", Header.AuxHdrLen.value()); + W.printNumber("Num FDEs", Header.NumFDEs.value()); + W.printNumber("Num FREs", Header.NumFREs.value()); + W.printNumber("FRE subsection length", Header.FRELen.value()); + W.printNumber("FDE subsection offset", Header.FDEOff.value()); + W.printNumber("FRE subsection offset", Header.FREOff.value()); + } +} + template void GNUELFDumper::printELFLinkerOptions() { OS << "printELFLinkerOptions not implemented!\n"; } diff --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp index 1a535ede0709..bd670aeab9ed 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.cpp +++ b/llvm/tools/llvm-readobj/ObjDumper.cpp @@ -102,9 +102,9 @@ void ObjDumper::printFileSummary(StringRef FileStr, object::ObjectFile &Obj, this->printLoadName(); } -static std::vector -getSectionRefsByNameOrIndex(const object::ObjectFile &Obj, - ArrayRef Sections) { +std::vector +ObjDumper::getSectionRefsByNameOrIndex(const object::ObjectFile &Obj, + ArrayRef Sections) { std::vector Ret; std::map> SecNames; std::map SecIndices; diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index a76afbe9c88c..1dc29661f717 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -139,6 +139,7 @@ public: virtual void printSectionDetails() {} virtual void printArchSpecificInfo() {} virtual void printMemtag() {} + virtual void printSectionsAsSFrame(ArrayRef Sections) {} // Only implemented for PE/COFF. virtual void printCOFFImports() { } @@ -190,6 +191,10 @@ public: protected: ScopedPrinter &W; + static std::vector + getSectionRefsByNameOrIndex(const object::ObjectFile &Obj, + ArrayRef Sections); + private: virtual void printSymbols(bool ExtraSymInfo) {} virtual void printSymbols(std::optional Comp) {} diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td index f95461aaca1a..48d43cc635a4 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 1231c02035d1..4c84ed701bb9 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) @@ -478,6 +480,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, Dumper->printNotes(); if (opts::Memtag) Dumper->printMemtag(); + if (!opts::SFrame.empty()) + Dumper->printSectionsAsSFrame(opts::SFrame); } if (Obj.isCOFF()) { if (opts::COFFImports)