mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
cf42e497aa
This commit introduces the Function Isolation Pass. We use the information provided by the Function Boundaries Detection Pass to organize the code that `revamb` places inside the `root` function in different LLVM functions. To do this we obviously need to introduce some changes and tricks to handle the execution of the translated program. The main idea is to have two different realms (one where the isolated functions live, one in which we have basically the old root function). We start the execution from the realm of the *non isolated* functions, and we transfer, as soon as possible, the execution to the *isolated functions* realm. We then have a fallback mechanism to restore the execution in the right place in the *non isolated* functions realm, and so on. The largest change, besides the re-organization of the code in different functions, is the use of the exception handling mechanism provided by the LLVM framework in order to be able to manage the switch between the two realms. We also introduce the `support.h` header file, which contains a couple of definitions used by `support.c` and that need to be shared with some of the components involved in the translation process. We have defined some helper functions, directly in C, that we use both for handling the exception mechanism and for giving extra debug informations when an exception is raised. The `revamb-dump` utility now supports the `-i` option to specify the path were to save the new LLVM module. The `translate` utility now supports the `-i` option that produces a binary in which the function isolation has been applied. We also introduced some tests that apply the function isolation pass to the `Runtime/` tests already present. In this way we can verify that the translation and the following function isolation preserve the behavior of the program. When serializing the new LLVM module we regenerate the metadata used for debug purposes, and for doing this, since we not longer have only the `root` function, we have changed some details in the `DebugHelper` class in order to be able to emit the metadata for all the functions of our interest in a single shot.
219 lines
6.3 KiB
C++
219 lines
6.3 KiB
C++
/// \file dump.cpp
|
|
/// \brief Standalone program to extract various information from the LLVM IR
|
|
/// generated by revamb
|
|
|
|
// Standard includes
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <memory>
|
|
|
|
// LLVM includes
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/IR/AssemblyAnnotationWriter.h"
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IRReader/IRReader.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
|
|
// Local includes
|
|
#include "argparse.h"
|
|
#include "collectcfg.h"
|
|
#include "collectfunctionboundaries.h"
|
|
#include "collectnoreturn.h"
|
|
#include "debug.h"
|
|
#include "debughelper.h"
|
|
#include "isolatefunctions.h"
|
|
#include "stackanalysis.h"
|
|
|
|
using namespace llvm;
|
|
|
|
struct ProgramParameters {
|
|
const char *InputPath;
|
|
const char *CFGPath;
|
|
const char *NoreturnPath;
|
|
const char *FunctionBoundariesPath;
|
|
const char *StackAnalysisPath;
|
|
const char *FunctionIsolationPath;
|
|
};
|
|
|
|
static const char *const Usage[] = {
|
|
"revamb-dump [options] INFILE",
|
|
nullptr,
|
|
};
|
|
|
|
static bool parseArgs(int Argc, const char *Argv[], ProgramParameters &Result) {
|
|
// Initialize argument parser
|
|
const char *DebugLoggingString = nullptr;
|
|
struct argparse Arguments;
|
|
struct argparse_option Options[] = {
|
|
OPT_HELP(),
|
|
OPT_STRING('d', "debug",
|
|
&DebugLoggingString,
|
|
"enable verbose logging."),
|
|
OPT_STRING('c', "cfg",
|
|
&Result.CFGPath,
|
|
"path where the CFG should be stored."),
|
|
OPT_STRING('n', "noreturn",
|
|
&Result.NoreturnPath,
|
|
"path where the list of noreturn basic blocks should be "
|
|
"stored."),
|
|
OPT_STRING('f', "functions-boundaries",
|
|
&Result.FunctionBoundariesPath,
|
|
"path where the list of function boundaries blocks should be "
|
|
"stored."),
|
|
OPT_STRING('s', "stack-analysis",
|
|
&Result.StackAnalysisPath,
|
|
"path where the result of the stack analysis should be stored."),
|
|
OPT_STRING('i', "functions-isolation",
|
|
&Result.FunctionIsolationPath,
|
|
"path where a new LLVM module containing the reorganization of "
|
|
"the basic blocks into the corresponding functions identified "
|
|
"by function boundaries analysis performed by revamb should be "
|
|
"stored."),
|
|
OPT_END(),
|
|
};
|
|
|
|
argparse_init(&Arguments, Options, Usage, 0);
|
|
argparse_describe(&Arguments, "\nrevamb-dump.",
|
|
"\nDump several high-level information from the "
|
|
"revamb-generated LLVM IR.\n");
|
|
Argc = argparse_parse(&Arguments, Argc, Argv);
|
|
|
|
if (DebugLoggingString != nullptr) {
|
|
DebuggingEnabled = true;
|
|
std::string Input(DebugLoggingString);
|
|
std::stringstream Stream(Input);
|
|
std::string Type;
|
|
while (std::getline(Stream, Type, ','))
|
|
enableDebugFeature(Type.c_str());
|
|
}
|
|
|
|
// Handle positional arguments
|
|
if (Argc != 1) {
|
|
fprintf(stderr, "Please specify one and only one input file.\n");
|
|
return false;
|
|
}
|
|
|
|
Result.InputPath = Argv[0];
|
|
|
|
return true;
|
|
}
|
|
|
|
class DumpPass : public FunctionPass {
|
|
public:
|
|
static char ID;
|
|
|
|
public:
|
|
DumpPass(ProgramParameters &Parameters) : FunctionPass(ID),
|
|
Parameters(Parameters) { }
|
|
|
|
bool runOnFunction(Function &F) override {
|
|
std::ofstream Output;
|
|
|
|
if (Parameters.CFGPath != nullptr) {
|
|
auto &Analysis = getAnalysis<CollectCFG>();
|
|
Analysis.serialize(pathToStream(Parameters.CFGPath, Output));
|
|
}
|
|
|
|
if (Parameters.NoreturnPath != nullptr) {
|
|
auto &Analysis = getAnalysis<CollectNoreturn>();
|
|
Analysis.serialize(pathToStream(Parameters.NoreturnPath, Output));
|
|
}
|
|
|
|
if (Parameters.FunctionBoundariesPath != nullptr) {
|
|
auto &Analysis = getAnalysis<CollectFunctionBoundaries>();
|
|
Analysis.serialize(pathToStream(Parameters.FunctionBoundariesPath,
|
|
Output));
|
|
}
|
|
|
|
if (Parameters.StackAnalysisPath != nullptr) {
|
|
auto &Analysis = getAnalysis<StackAnalysis::StackAnalysis>();
|
|
Analysis.serialize(pathToStream(Parameters.StackAnalysisPath, Output));
|
|
}
|
|
|
|
if (Parameters.FunctionIsolationPath != nullptr) {
|
|
auto &Analysis = getAnalysis<IsolateFunctions>();
|
|
Module *ModifiedModule = Analysis.getModule();
|
|
dumpModule(ModifiedModule, Parameters.FunctionIsolationPath);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
AU.setPreservesAll();
|
|
|
|
if (Parameters.CFGPath != nullptr)
|
|
AU.addRequired<CollectCFG>();
|
|
|
|
if (Parameters.NoreturnPath != nullptr)
|
|
AU.addRequired<CollectNoreturn>();
|
|
|
|
if (Parameters.FunctionBoundariesPath != nullptr)
|
|
AU.addRequired<CollectFunctionBoundaries>();
|
|
|
|
if (Parameters.StackAnalysisPath != nullptr)
|
|
AU.addRequired<StackAnalysis::StackAnalysis>();
|
|
|
|
if (Parameters.FunctionIsolationPath != nullptr)
|
|
AU.addRequired<IsolateFunctions>();
|
|
|
|
}
|
|
|
|
private:
|
|
std::ostream &pathToStream(const char *Path, std::ofstream &File) {
|
|
if (Path[0] == '-' && Path[1] == '\0') {
|
|
return std::cout;
|
|
} else {
|
|
if (File.is_open())
|
|
File.close();
|
|
File.open(Path);
|
|
return File;
|
|
}
|
|
}
|
|
|
|
void dumpModule(Module *Module, const char *Path) {
|
|
std::ofstream Output;
|
|
|
|
// If output path is `-` print on stdout
|
|
// TODO: this solution is not portable, make DebugHelper accept streams
|
|
if (Path[0] == '-' && Path[1] == '\0') {
|
|
Path = "/dev/stdout";
|
|
}
|
|
|
|
// Initialize the debug helper object
|
|
DebugHelper Debug(Path, Path, Module, DebugInfoType::LLVMIR);
|
|
Debug.generateDebugInfo();
|
|
}
|
|
|
|
private:
|
|
ProgramParameters &Parameters;
|
|
};
|
|
|
|
char DumpPass::ID = 0;
|
|
|
|
int main(int argc, const char *argv[]) {
|
|
ProgramParameters Parameters = { nullptr, nullptr };
|
|
|
|
if (!parseArgs(argc, argv, Parameters))
|
|
return EXIT_FAILURE;
|
|
|
|
LLVMContext &Context = getGlobalContext();
|
|
SMDiagnostic Err;
|
|
std::unique_ptr<Module> TheModule = parseIRFile(Parameters.InputPath,
|
|
Err,
|
|
Context);
|
|
|
|
if (!TheModule) {
|
|
fprintf(stderr, "Couldn't load the LLVM IR.");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
legacy::FunctionPassManager FPM(TheModule.get());
|
|
FPM.add(new DumpPass(Parameters));
|
|
FPM.run(*TheModule->getFunction("root"));
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|