Index: test/tools/llvm-vtabledump/trivial.test =================================================================== --- /dev/null +++ test/tools/llvm-vtabledump/trivial.test @@ -0,0 +1,7 @@ +RUN: llvm-vtabledump %p/Inputs/trivial.obj.coff-i386 \ +RUN: | FileCheck %s + +CHECK: ??_7S@@6B@[0]: ??_R4S@@6B@ +CHECK-NEXT: ??_7S@@6B@[4]: ??_GS@@UAEPAXI@Z +CHECK-NEXT: ??_8S@@7B@[0]: -4 +CHECK-NEXT: ??_8S@@7B@[4]: 4 Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -30,6 +30,7 @@ add_llvm_tool_subdirectory(llvm-readobj) add_llvm_tool_subdirectory(llvm-rtdyld) add_llvm_tool_subdirectory(llvm-dwarfdump) +add_llvm_tool_subdirectory(llvm-vtabledump) if( LLVM_USE_INTEL_JITEVENTS ) add_llvm_tool_subdirectory(llvm-jitlistener) else() Index: tools/Makefile =================================================================== --- tools/Makefile +++ tools/Makefile @@ -31,7 +31,8 @@ lli llvm-extract llvm-mc bugpoint llvm-bcanalyzer llvm-diff \ macho-dump llvm-objdump llvm-readobj llvm-rtdyld \ llvm-dwarfdump llvm-cov llvm-size llvm-stress llvm-mcmarkup \ - llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test + llvm-profdata llvm-symbolizer obj2yaml yaml2obj llvm-c-test \ + llvm-vtabledump # If Intel JIT Events support is configured, build an extra tool to test it. ifeq ($(USE_INTEL_JITEVENTS), 1) Index: tools/llvm-vtabledump/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llvm-vtabledump/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Object + Support + ) + +add_llvm_tool(llvm-vtabledump + llvm-vtabledump.cpp + Error.cpp + ) Index: tools/llvm-vtabledump/Error.h =================================================================== --- /dev/null +++ tools/llvm-vtabledump/Error.h @@ -0,0 +1,39 @@ +//===- Error.h - system_error extensions for llvm-vtabledump ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This declares a new error_category for the llvm-vtabledump tool. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_VTABLEDUMP_ERROR_H +#define LLVM_VTABLEDUMP_ERROR_H + +#include + +namespace llvm { +const std::error_category &vtabledump_category(); + +enum class vtabledump_error { + success = 0, + file_not_found, + unrecognized_file_format, +}; + +inline std::error_code make_error_code(vtabledump_error e) { + return std::error_code(static_cast(e), vtabledump_category()); +} + +} // namespace llvm + +namespace std { +template <> +struct is_error_code_enum : std::true_type {}; +} + +#endif Index: tools/llvm-vtabledump/Error.cpp =================================================================== --- /dev/null +++ tools/llvm-vtabledump/Error.cpp @@ -0,0 +1,49 @@ +//===- Error.cpp - system_error extensions for llvm-vtabledump --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines a new error_category for the llvm-vtabledump tool. +// +//===----------------------------------------------------------------------===// + +#include "Error.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +namespace { +class _vtabledump_error_category : public std::error_category { +public: + const char *name() const LLVM_NOEXCEPT override; + std::string message(int ev) const override; +}; +} // namespace + +const char *_vtabledump_error_category::name() const { + return "llvm.vtabledump"; +} + +std::string _vtabledump_error_category::message(int EV) const { + switch (static_cast(EV)) { + case vtabledump_error::success: + return "Success"; + case vtabledump_error::file_not_found: + return "No such file."; + case vtabledump_error::unrecognized_file_format: + return "Unrecognized file type."; + } + llvm_unreachable("An enumerator of vtabledump_error does not have a message " + "defined."); +} + +namespace llvm { +const std::error_category &vtabledump_category() { + static _vtabledump_error_category o; + return o; +} +} // namespace llvm Index: tools/llvm-vtabledump/LLVMBuild.txt =================================================================== --- /dev/null +++ tools/llvm-vtabledump/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-vtabledump/LLVMBuild.txt --------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-vtabledump +parent = Tools +required_libraries = all-targets BitReader Object Index: tools/llvm-vtabledump/Makefile =================================================================== --- /dev/null +++ tools/llvm-vtabledump/Makefile @@ -0,0 +1,18 @@ +##===- tools/llvm-vtabledump/Makefile ----------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../.. +TOOLNAME := llvm-vtabledump +LINK_COMPONENTS := bitreader object all-targets + +# This tool has no plugins, optimize startup time. +TOOL_NO_EXPORTS := 1 + +include $(LEVEL)/Makefile.common + Index: tools/llvm-vtabledump/llvm-vtabledump.h =================================================================== --- /dev/null +++ tools/llvm-vtabledump/llvm-vtabledump.h @@ -0,0 +1,23 @@ +//===-- llvm-vtabledump.h -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_READ_OBJ_H +#define LLVM_TOOLS_READ_OBJ_H + +#include "llvm/Support/CommandLine.h" +#include + +namespace opts { + extern llvm::cl::list InputFilenames; +} // namespace opts + +#define LLVM_VTABLEDUMP_ENUM_ENT(ns, enum) \ + { #enum, ns::enum } + +#endif Index: tools/llvm-vtabledump/llvm-vtabledump.cpp =================================================================== --- /dev/null +++ tools/llvm-vtabledump/llvm-vtabledump.cpp @@ -0,0 +1,201 @@ +//===- llvm-vtabledump.cpp - Dump vtables in an Object File ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Dumps VTables resident in object files and archives. Note, it currently only +// supports MS-ABI style object files. +// +//===----------------------------------------------------------------------===// + +#include "llvm-vtabledump.h" +#include "Error.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include +#include +#include + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support; + +namespace opts { +cl::list InputFilenames(cl::Positional, + cl::desc(""), + cl::ZeroOrMore); +} // namespace opts + +static int ReturnValue = EXIT_SUCCESS; + +namespace llvm { + +bool error(std::error_code EC) { + if (!EC) + return false; + + ReturnValue = EXIT_FAILURE; + outs() << "\nError reading file: " << EC.message() << ".\n"; + outs().flush(); + return true; +} + +} // namespace llvm + +static void reportError(StringRef Input, std::error_code EC) { + if (Input == "-") + Input = ""; + + errs() << Input << ": " << EC.message() << "\n"; + errs().flush(); + ReturnValue = EXIT_FAILURE; +} + +static void reportError(StringRef Input, StringRef Message) { + if (Input == "-") + Input = ""; + + errs() << Input << ": " << Message << "\n"; + ReturnValue = EXIT_FAILURE; +} + +static void dumpVTables(const ObjectFile *Obj) { + std::map, StringRef> VFTableEntries; + std::map> VBTables; + for (const object::SymbolRef &Sym : Obj->symbols()) { + StringRef SymName; + if (error(Sym.getName(SymName))) + return; + if (SymName.startswith("??_7")) { + object::section_iterator SecI(Obj->section_begin()); + if (error(Sym.getSection(SecI))) + return; + if (SecI == Obj->section_end()) + continue; + for (const object::RelocationRef &Reloc : SecI->relocations()) { + const object::symbol_iterator RelocSymI = Reloc.getSymbol(); + if (RelocSymI == Obj->symbol_end()) + continue; + StringRef RelocSymName; + if (error(RelocSymI->getName(RelocSymName))) + return; + uint64_t Offset; + if (error(Reloc.getOffset(Offset))) + return; + VFTableEntries[std::make_pair(SymName, Offset)] = RelocSymName; + } + } else if (SymName.startswith("??_8")) { + object::section_iterator SecI(Obj->section_begin()); + if (error(Sym.getSection(SecI))) + return; + if (SecI == Obj->section_end()) + continue; + StringRef SecContents; + if (error(SecI->getContents(SecContents))) + return; + + ArrayRef VBTableData( + reinterpret_cast(SecContents.data()), + SecContents.size() / sizeof(aligned_little32_t)); + VBTables[SymName] = VBTableData; + } + } + for ( + const std::pair, StringRef> &VFTableEntry : + VFTableEntries) { + StringRef VFTableName = VFTableEntry.first.first; + uint64_t Offset = VFTableEntry.first.second; + StringRef SymName = VFTableEntry.second; + outs() << VFTableName << '[' << Offset << "]: " << SymName << '\n'; + } + for (const std::pair> &VBTable : + VBTables) { + StringRef VBTableName = VBTable.first; + uint32_t Idx = 0; + for (aligned_little32_t Offset : VBTable.second) { + outs() << VBTableName << '[' << Idx << "]: " << Offset << '\n'; + Idx += 4; + } + } +} + +/// @brief Dumps each vtable file in \a Arc; +static void dumpArchive(const Archive *Arc) { + for (Archive::child_iterator ArcI = Arc->child_begin(), + ArcE = Arc->child_end(); + ArcI != ArcE; ++ArcI) { + ErrorOr> ChildOrErr = ArcI->getAsBinary(); + if (std::error_code EC = ChildOrErr.getError()) { + // Ignore non-object files. + if (EC != object_error::invalid_file_type) + reportError(Arc->getFileName(), EC.message()); + continue; + } + + if (ObjectFile *Obj = dyn_cast(&*ChildOrErr.get())) + dumpVTables(Obj); + else + reportError(Arc->getFileName(), + vtabledump_error::unrecognized_file_format); + } +} + +/// @brief Opens \a File and dumps the vtables in it. +static void dumpInput(StringRef File) { + // If file isn't stdin, check that it exists. + if (File != "-" && !sys::fs::exists(File)) { + reportError(File, vtabledump_error::file_not_found); + return; + } + + // Attempt to open the binary. + ErrorOr BinaryOrErr = createBinary(File); + if (std::error_code EC = BinaryOrErr.getError()) { + reportError(File, EC); + return; + } + std::unique_ptr Binary(BinaryOrErr.get()); + + if (Archive *Arc = dyn_cast(Binary.get())) + dumpArchive(Arc); + else if (ObjectFile *Obj = dyn_cast(Binary.get())) + dumpVTables(Obj); + else + reportError(File, vtabledump_error::unrecognized_file_format); +} + +int main(int argc, const char *argv[]) { + sys::PrintStackTraceOnErrorSignal(); + PrettyStackTraceProgram X(argc, argv); + llvm_shutdown_obj Y; + + // Initialize targets. + llvm::InitializeAllTargetInfos(); + + // Register the target printer for --version. + cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); + + cl::ParseCommandLineOptions(argc, argv, "LLVM VTable Dumper\n"); + + // Default to stdin if no filename is specified. + if (opts::InputFilenames.size() == 0) + opts::InputFilenames.push_back("-"); + + std::for_each(opts::InputFilenames.begin(), opts::InputFilenames.end(), + dumpInput); + + return ReturnValue; +}