From 8a07e6776715e2ce465f28c6f73f24d38e238a07 Mon Sep 17 00:00:00 2001 From: Ivan Krysak Date: Tue, 3 May 2022 13:18:49 +0300 Subject: [PATCH] Add `revng yield assembly` tool --- python/revng/cli/commands_registry.py | 2 + tools/CMakeLists.txt | 1 + tools/yield/AssemblyHTML.cpp | 37 +++++++++++ tools/yield/AssemblyPlain.cpp | 38 ++++++++++++ tools/yield/CMakeLists.txt | 21 +++++++ tools/yield/Common.cpp | 89 +++++++++++++++++++++++++++ tools/yield/Common.h | 35 +++++++++++ 7 files changed, 223 insertions(+) create mode 100644 tools/yield/AssemblyHTML.cpp create mode 100644 tools/yield/AssemblyPlain.cpp create mode 100644 tools/yield/CMakeLists.txt create mode 100644 tools/yield/Common.cpp create mode 100644 tools/yield/Common.h diff --git a/python/revng/cli/commands_registry.py b/python/revng/cli/commands_registry.py index 6ab682ca0..26fdf156d 100644 --- a/python/revng/cli/commands_registry.py +++ b/python/revng/cli/commands_registry.py @@ -181,3 +181,5 @@ class CommandsRegistry: commands_registry = CommandsRegistry() commands_registry.define_namespace(("model",)) commands_registry.define_namespace(("model", "import")) +commands_registry.define_namespace(("yield",)) +commands_registry.define_namespace(("yield", "assembly")) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 7d6d6a312..f1a4b1452 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory(model) add_subdirectory(efa) add_subdirectory(pipeline) add_subdirectory(link-for-translation) +add_subdirectory(yield) diff --git a/tools/yield/AssemblyHTML.cpp b/tools/yield/AssemblyHTML.cpp new file mode 100644 index 000000000..0bcd44e75 --- /dev/null +++ b/tools/yield/AssemblyHTML.cpp @@ -0,0 +1,37 @@ +/// \file AssemblyHTML.cpp +/// \brief + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "revng/EarlyFunctionAnalysis/IRHelpers.h" +#include "revng/Support/FunctionTags.h" +#include "revng/Yield/Assembly/DisassemblyHelper.h" +#include "revng/Yield/HTML.h" + +#include "Common.h" + +int main(int ArgC, char *ArgV[]) { + auto [IR, Model, Binary, Result, _] = parseCommandLineOptions(ArgC, ArgV); + + // Define the helper object to store the disassembly pipeline. + // This allows it to only be created once. + DissassemblyHelper Helper; + + for (const auto &LLVMFunction : FunctionTags::Isolated.functions(&IR)) { + auto Metadata = extractFunctionMetadata(&LLVMFunction); + auto ModelFunctionIterator = Model.Functions.find(Metadata->Entry); + revng_assert(ModelFunctionIterator != Model.Functions.end()); + const auto &Function = *ModelFunctionIterator; + + auto Disassembled = Helper.disassemble(Function, *Metadata, Binary); + + auto HTML = yield::html::assembly(Disassembled, *Metadata, Model); + Result.insert_or_assign(Function.Entry, std::move(HTML)); + } + + llvm::Error Error = Result.serialize(llvm::outs()); + revng_assert(!bool(Error)); + return EXIT_SUCCESS; +} diff --git a/tools/yield/AssemblyPlain.cpp b/tools/yield/AssemblyPlain.cpp new file mode 100644 index 000000000..cb443ed2f --- /dev/null +++ b/tools/yield/AssemblyPlain.cpp @@ -0,0 +1,38 @@ +/// \file AssemblyPlain.cpp +/// \brief + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "revng/EarlyFunctionAnalysis/IRHelpers.h" +#include "revng/Support/FunctionTags.h" +#include "revng/Yield/Assembly/DisassemblyHelper.h" +#include "revng/Yield/HTML.h" +#include "revng/Yield/Plain.h" + +#include "Common.h" + +int main(int ArgC, char *ArgV[]) { + auto [IR, Model, Binary, Result, _] = parseCommandLineOptions(ArgC, ArgV); + + // Define the helper object to store the disassembly pipeline. + // This allows it to only be created once. + DissassemblyHelper Helper; + + for (const auto &LLVMFunction : FunctionTags::Isolated.functions(&IR)) { + auto Metadata = extractFunctionMetadata(&LLVMFunction); + auto ModelFunctionIterator = Model.Functions.find(Metadata->Entry); + revng_assert(ModelFunctionIterator != Model.Functions.end()); + const auto &Function = *ModelFunctionIterator; + + auto Disassembled = Helper.disassemble(Function, *Metadata, Binary); + + auto Plain = yield::plain::assembly(Disassembled, *Metadata, Model); + Result.insert_or_assign(Function.Entry, std::move(Plain)); + } + + llvm::Error Error = Result.serialize(llvm::outs()); + revng_assert(Error); + return EXIT_SUCCESS; +} diff --git a/tools/yield/CMakeLists.txt b/tools/yield/CMakeLists.txt new file mode 100644 index 000000000..66086fe66 --- /dev/null +++ b/tools/yield/CMakeLists.txt @@ -0,0 +1,21 @@ +# +# This file is distributed under the MIT License. See LICENSE.md for details. +# + +macro(add_tool tool_name sources) + revng_add_executable(${tool_name} ${sources} Common.cpp) + + llvm_map_components_to_libnames(LLVM_LIBRARIES Core Support) + target_link_libraries( + ${tool_name} + revngEarlyFunctionAnalysis + revngLift + revngModel + revngPipes + revngSupport + revngYield + ${LLVM_LIBRARIES}) +endmacro() + +add_tool(revng-yield-assembly-html AssemblyHTML.cpp) +add_tool(revng-yield-assembly-plain AssemblyPlain.cpp) diff --git a/tools/yield/Common.cpp b/tools/yield/Common.cpp new file mode 100644 index 000000000..545bb4de3 --- /dev/null +++ b/tools/yield/Common.cpp @@ -0,0 +1,89 @@ +/// \file Common.cpp +/// \brief + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include "revng/Model/RawBinaryView.h" +#include "revng/Pipes/Kinds.h" +#include "revng/Support/MetaAddress/YAMLTraits.h" +#include "revng/Support/YAMLTraits.h" + +#include "Common.h" + +namespace options { + +using namespace llvm::cl; + +OptionCategory ThisToolCategory("Tool options", ""); + +static opt BinaryPath(llvm::cl::Positional, + llvm::cl::Required, + desc(""), + value_desc("binary"), + cat(ThisToolCategory)); + +static opt LLVMModulePath(llvm::cl::Positional, + llvm::cl::Required, + desc(""), + value_desc("llvm-module"), + cat(ThisToolCategory)); + +static opt ModelPath(llvm::cl::Positional, + desc(""), + value_desc("model"), + init(""), + cat(ThisToolCategory)); + +} // namespace options + +static revng::pipes::StringMapContainer createMap() { + using C = revng::pipes::StringMapContainer; + return C("", "", pipeline::Kind{ "", &revng::pipes::RootRank }); +} + +ReturnValueType parseCommandLineOptions(int Argc, char *Argv[]) { + llvm::cl::HideUnrelatedOptions({ &options::ThisToolCategory }); + llvm::cl::ParseCommandLineOptions(Argc, Argv); + + llvm::ExitOnError ExitOnError; + ObjectLifetimeController Result; + + Result.Context = std::make_unique(); + + llvm::SMDiagnostic Diagnostic; + Result.IR = llvm::parseIRFile(options::LLVMModulePath, + Diagnostic, + *Result.Context); + revng_assert(Result.IR != nullptr); + + using ModelBinaryTree = TupleTree; + if (options::ModelPath.empty()) { + revng_assert(hasModel(*Result.IR)); + Result.Model = std::make_unique(loadModel(*Result.IR)); + } else { + auto ErrorOrModel = ModelBinaryTree::fromFile(options::ModelPath); + auto Model = ExitOnError(llvm::errorOrToExpected(std::move(ErrorOrModel))); + Result.Model = std::make_unique(std::move(Model)); + } + revng_assert(Result.Model != nullptr); + + auto [BinView, + Storage] = ExitOnError(loadBinary(**Result.Model, options::BinaryPath)); + Result.Binary = std::move(Storage); + revng_assert(Result.Binary != nullptr); + + return ReturnValueType(*Result.IR, + **Result.Model, + BinView, + createMap(), + std::move(Result)); +} diff --git a/tools/yield/Common.h b/tools/yield/Common.h new file mode 100644 index 000000000..c45bf8bd4 --- /dev/null +++ b/tools/yield/Common.h @@ -0,0 +1,35 @@ +#pragma once + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include + +#include "revng/Lift/LoadBinaryPass.h" +#include "revng/Pipes/StringMapContainer.h" +#include "revng/TupleTree/TupleTree.h" + +namespace llvm { +class LLVMContext; +class MemoryBuffer; +class Module; +} // namespace llvm + +namespace model { +class Binary; +} + +struct ObjectLifetimeController { + std::unique_ptr Context = {}; + std::unique_ptr IR = {}; + std::unique_ptr> Model = {}; + std::unique_ptr Binary = {}; +}; + +using ReturnValueType = std::tuple; +ReturnValueType parseCommandLineOptions(int Argc, char *Argv[]);