diff --git a/test/tools/llvm-vtabledump/Inputs/trivial.obj.coff-i386 b/test/tools/llvm-vtabledump/Inputs/trivial.obj.coff-i386 new file mode 100644 index 00000000000..3b9395583eb Binary files /dev/null and b/test/tools/llvm-vtabledump/Inputs/trivial.obj.coff-i386 differ diff --git a/test/tools/llvm-vtabledump/trivial.test b/test/tools/llvm-vtabledump/trivial.test new file mode 100644 index 00000000000..e24d6ad842e --- /dev/null +++ b/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 diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 846ad1e25d6..acc4eb12860 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -30,6 +30,7 @@ add_llvm_tool_subdirectory(llvm-objdump) 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() diff --git a/tools/Makefile b/tools/Makefile index 97ad99a9d1a..012270d49ee 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -31,7 +31,8 @@ PARALLEL_DIRS := opt llvm-as llvm-dis llc llvm-ar llvm-nm llvm-link \ 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) diff --git a/tools/llvm-vtabledump/CMakeLists.txt b/tools/llvm-vtabledump/CMakeLists.txt new file mode 100644 index 00000000000..4fe205b6efd --- /dev/null +++ b/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 + ) diff --git a/tools/llvm-vtabledump/Error.cpp b/tools/llvm-vtabledump/Error.cpp new file mode 100644 index 00000000000..c5de8951636 --- /dev/null +++ b/tools/llvm-vtabledump/Error.cpp @@ -0,0 +1,43 @@ +//===- 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 { return "llvm.vtabledump"; } + std::string message(int ev) const override { + 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 + +namespace llvm { +const std::error_category &vtabledump_category() { + static vtabledump_error_category o; + return o; +} +} // namespace llvm diff --git a/tools/llvm-vtabledump/Error.h b/tools/llvm-vtabledump/Error.h new file mode 100644 index 00000000000..73e0d1a498b --- /dev/null +++ b/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_TOOLS_VTABLEDUMP_ERROR_H +#define LLVM_TOOLS_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 diff --git a/tools/llvm-vtabledump/LLVMBuild.txt b/tools/llvm-vtabledump/LLVMBuild.txt new file mode 100644 index 00000000000..6a3cbfff4c1 --- /dev/null +++ b/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 diff --git a/tools/llvm-vtabledump/Makefile b/tools/llvm-vtabledump/Makefile new file mode 100644 index 00000000000..596c64c67b6 --- /dev/null +++ b/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 + diff --git a/tools/llvm-vtabledump/llvm-vtabledump.cpp b/tools/llvm-vtabledump/llvm-vtabledump.cpp new file mode 100644 index 00000000000..ce2e9b6ddff --- /dev/null +++ b/tools/llvm-vtabledump/llvm-vtabledump.cpp @@ -0,0 +1,203 @@ +//===- llvm-vtabledump.cpp - Dump vtables in an Object File -----*- C++ -*-===// +// +// 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/ADT/StringMap.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, StringRef Message) { + if (Input == "-") + Input = ""; + + errs() << Input << ": " << Message << "\n"; + errs().flush(); + ReturnValue = EXIT_FAILURE; +} + +static void reportError(StringRef Input, std::error_code EC) { + reportError(Input, EC.message()); +} + +static void dumpVTables(const ObjectFile *Obj) { + std::map, StringRef> VFTableEntries; + StringMap> VBTables; + for (const object::SymbolRef &Sym : Obj->symbols()) { + StringRef SymName; + if (error(Sym.getName(SymName))) + return; + // VFTables in the MS-ABI start with '??_7' and are contained within their + // own COMDAT section. We then determine the contents of the VFTable by + // looking at each relocation in the section. + if (SymName.startswith("??_7")) { + object::section_iterator SecI(Obj->section_begin()); + if (error(Sym.getSection(SecI))) + return; + if (SecI == Obj->section_end()) + continue; + // Each relocation either names a virtual method or a thunk. We note the + // offset into the section and the symbol used for the relocation. + 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; + } + } + // VBTables in the MS-ABI start with '??_8' and are filled with 32-bit + // offsets of virtual bases. + 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 StringMapEntry> &VBTable : VBTables) { + StringRef VBTableName = VBTable.getKey(); + uint32_t Idx = 0; + for (aligned_little32_t Offset : VBTable.getValue()) { + outs() << VBTableName << '[' << Idx << "]: " << Offset << '\n'; + Idx += sizeof(aligned_little32_t); + } + } +} + +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); + } +} + +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; +} diff --git a/tools/llvm-vtabledump/llvm-vtabledump.h b/tools/llvm-vtabledump/llvm-vtabledump.h new file mode 100644 index 00000000000..16a752bcb6f --- /dev/null +++ b/tools/llvm-vtabledump/llvm-vtabledump.h @@ -0,0 +1,23 @@ +//===-- llvm-vtabledump.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_VTABLEDUMP_H +#define LLVM_TOOLS_VTABLEDUMP_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