Index: llvm/test/tools/llvm-readobj/ELF/output-style.test =================================================================== --- llvm/test/tools/llvm-readobj/ELF/output-style.test +++ llvm/test/tools/llvm-readobj/ELF/output-style.test @@ -1,4 +1,4 @@ ## Error for an unknown output style. RUN: not llvm-readobj --elf-output-style=unknown 2>&1 | FileCheck %s -CHECK: error: --elf-output-style value should be either 'LLVM' or 'GNU' +CHECK: error: --elf-output-style value should be either 'LLVM', 'GNU', or 'JSON' but was 'unknown' Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -31,6 +31,7 @@ #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Object/Archive.h" #include "llvm/Object/ELF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" @@ -548,6 +549,9 @@ assert(&this->W.getOStream() == &llvm::fouts()); } + void printFileSummary(StringRef FileStr, ObjectFile &Obj, + ArrayRef InputFilenames, + const Archive *A) override; void printFileHeaders() override; void printGroupSections() override; void printRelocations() override; @@ -705,9 +709,27 @@ void printMipsPLT(const MipsGOTParser &Parser) override; void printMipsABIFlags() override; +protected: ScopedPrinter &W; }; +// JSONELFDumper shares most of the same implementation as LLVMELFDumper except +// it uses a JSONScopedPrinter. +template class JSONELFDumper : public LLVMELFDumper { +public: + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + + JSONELFDumper(const object::ELFObjectFile &ObjF, ScopedPrinter &Writer) + : LLVMELFDumper(ObjF, Writer) {} + + void printFileSummary(StringRef FileStr, ObjectFile &Obj, + ArrayRef InputFilenames, + const Archive *A) override; + +private: + std::unique_ptr FileScope; +}; + } // end anonymous namespace namespace llvm { @@ -717,6 +739,8 @@ createELFDumper(const ELFObjectFile &Obj, ScopedPrinter &Writer) { if (opts::Output == opts::GNU) return std::make_unique>(Obj, Writer); + else if (opts::Output == opts::JSON) + return std::make_unique>(Obj, Writer); return std::make_unique>(Obj, Writer); } @@ -3231,6 +3255,16 @@ return nullptr; } +template +void GNUELFDumper::printFileSummary(StringRef FileStr, ObjectFile &Obj, + ArrayRef InputFilenames, + const Archive *A) { + if (InputFilenames.size() > 1 || A) { + this->W.startLine() << "\n"; + this->W.printString("File", FileStr); + } +} + template void GNUELFDumper::printFileHeaders() { const Elf_Ehdr &e = this->Obj.getHeader(); OS << "ELF Header:\n"; @@ -7299,3 +7333,18 @@ W.printFlags("Flags 1", Flags->flags1, makeArrayRef(ElfMipsFlags1)); W.printHex("Flags 2", Flags->flags2); } + +template +void JSONELFDumper::printFileSummary(StringRef FileStr, ObjectFile &Obj, + ArrayRef InputFilenames, + const Archive *A) { + FileScope = std::make_unique(this->W, FileStr); + DictScope D(this->W, "FileSummary"); + this->W.printString("File", FileStr); + this->W.printString("Format", Obj.getFileFormatName()); + this->W.printString("Arch", Triple::getArchTypeName(Obj.getArch())); + this->W.printString( + "AddressSize", + std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress()))); + this->printLoadName(); +} Index: llvm/tools/llvm-readobj/ObjDumper.h =================================================================== --- llvm/tools/llvm-readobj/ObjDumper.h +++ llvm/tools/llvm-readobj/ObjDumper.h @@ -20,6 +20,7 @@ namespace llvm { namespace object { +class Archive; class COFFImportFile; class ObjectFile; class XCOFFObjectFile; @@ -39,6 +40,9 @@ virtual bool canDumpContent() { return true; } + virtual void printFileSummary(StringRef FileStr, object::ObjectFile &Obj, + ArrayRef InputFilenames, + const object::Archive *A); virtual void printFileHeaders() = 0; virtual void printSectionHeaders() = 0; virtual void printRelocations() = 0; Index: llvm/tools/llvm-readobj/ObjDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ObjDumper.cpp +++ llvm/tools/llvm-readobj/ObjDumper.cpp @@ -13,6 +13,7 @@ #include "ObjDumper.h" #include "llvm-readobj.h" +#include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Error.h" #include "llvm/Support/FormatVariadic.h" @@ -85,6 +86,18 @@ } } +void ObjDumper::printFileSummary(StringRef FileStr, object::ObjectFile &Obj, + ArrayRef InputFilenames, + const object::Archive *A) { + W.startLine() << "\n"; + W.printString("File", FileStr); + W.printString("Format", Obj.getFileFormatName()); + W.printString("Arch", Triple::getArchTypeName(Obj.getArch())); + W.printString("AddressSize", + std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress()))); + this->printLoadName(); +} + static std::vector getSectionRefsByNameOrIndex(const object::ObjectFile &Obj, ArrayRef Sections) { Index: llvm/tools/llvm-readobj/llvm-readobj.h =================================================================== --- llvm/tools/llvm-readobj/llvm-readobj.h +++ llvm/tools/llvm-readobj/llvm-readobj.h @@ -39,7 +39,7 @@ extern bool RawRelr; extern bool CodeViewSubsectionBytes; extern bool Demangle; -enum OutputStyleTy { LLVM, GNU }; +enum OutputStyleTy { LLVM, GNU, JSON, UNKNOWN }; extern OutputStyleTy Output; } // namespace opts Index: llvm/tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- llvm/tools/llvm-readobj/llvm-readobj.cpp +++ llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -230,13 +230,17 @@ opts::DynamicTable = Args.hasArg(OPT_dynamic_table); opts::ELFLinkerOptions = Args.hasArg(OPT_elf_linker_options); if (Arg *A = Args.getLastArg(OPT_elf_output_style_EQ)) { - StringRef V(A->getValue()); - if (V == "LLVM") - opts::Output = opts::OutputStyleTy::LLVM; - else if (V == "GNU") - opts::Output = opts::OutputStyleTy::GNU; - else - error("--elf-output-style value should be either 'LLVM' or 'GNU'"); + std::string OutputStyleChoice = A->getValue(); + opts::Output = StringSwitch(OutputStyleChoice) + .Case("LLVM", opts::OutputStyleTy::LLVM) + .Case("GNU", opts::OutputStyleTy::GNU) + .Case("JSON", opts::OutputStyleTy::JSON) + .Default(opts::OutputStyleTy::UNKNOWN); + if (opts::Output == opts::OutputStyleTy::UNKNOWN) { + error("--elf-output-style value should be either 'LLVM', 'GNU', or " + "'JSON' but was '" + + OutputStyleChoice + "'"); + } } opts::GnuHashTable = Args.hasArg(OPT_gnu_hash_table); opts::HashSymbols = Args.hasArg(OPT_hash_symbols); @@ -333,18 +337,7 @@ reportError(DumperOrErr.takeError(), FileStr); Dumper = (*DumperOrErr).get(); - if (opts::Output == opts::LLVM || opts::InputFilenames.size() > 1 || A) { - Writer.startLine() << "\n"; - Writer.printString("File", FileStr); - } - if (opts::Output == opts::LLVM) { - Writer.printString("Format", Obj.getFileFormatName()); - Writer.printString("Arch", Triple::getArchTypeName(Obj.getArch())); - Writer.printString( - "AddressSize", - std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress()))); - Dumper->printLoadName(); - } + Dumper->printFileSummary(FileStr, Obj, opts::InputFilenames, A); if (opts::FileHeaders) Dumper->printFileHeaders(); @@ -550,6 +543,12 @@ OwningBinary(std::move(Bin), std::move(Buffer))); } +std::unique_ptr createWriter() { + if (opts::Output == opts::JSON) + return std::make_unique(fouts()); + return std::make_unique(fouts()); +} + int main(int argc, char *argv[]) { InitLLVM X(argc, argv); BumpPtrAllocator A; @@ -610,16 +609,20 @@ opts::SectionHeaders = true; } - ScopedPrinter Writer(fouts()); + std::unique_ptr Writer = createWriter(); + std::unique_ptr D; + if (opts::Output == opts::JSON) + D = std::make_unique(*Writer.get()); + for (const std::string &I : opts::InputFilenames) - dumpInput(I, Writer); + dumpInput(I, *Writer.get()); if (opts::CodeViewMergedTypes) { if (opts::CodeViewEnableGHash) - dumpCodeViewMergedTypes(Writer, CVTypes.GlobalIDTable.records(), + dumpCodeViewMergedTypes(*Writer.get(), CVTypes.GlobalIDTable.records(), CVTypes.GlobalTypeTable.records()); else - dumpCodeViewMergedTypes(Writer, CVTypes.IDTable.records(), + dumpCodeViewMergedTypes(*Writer.get(), CVTypes.IDTable.records(), CVTypes.TypeTable.records()); }