Files
lifting-bits-mcsema/mcsema/BC/Function.cpp
T
Peter Goodman 8a9856ada3 Klee maze example (#369)
* In progress. Working on an example of using KLEE on a Maze, but with the maze program being compiled to x86, amd64, and aarch64.

* Making lots of progress on getting lifting and runnning an aarch64 maze program on amd64, but using --explicit_args. The key thing I'm working through right now is a jump offset table, but where the offset is a block pc, rather than a table base. Also adding various bits of code here and there to making runnning with klee more directly doable, and working on a debugging facility to track down when the emulated program counter gets out of sync with the original program.

* Fixed a subtle @PAGE and @PAGEOFF-related reference bug on AArch64. Partially disabled the special jump offset table handling I had in table.py, as it doesn't (yet) handle the shifted table values. However, I still have the code there, so that it can recognize that a basic block address is used as a possible offset, so that I can remove the block address as a reference, which permits a new heuristic on the C++ side to work. On the C++ side, when there's a jump instruction that isn't associated with a cross-reference flow, I try to auto-augment it with addition switch cases, targeting blocks with no predecessors (as present in the CFG). This seems to work reasonably well.

* Improved the scripts and updated the READMEs.

* Minor rephrase

* Minor rephrase
2018-01-13 23:47:55 -05:00

735 lines
24 KiB
C++

/*
* Copyright (c) 2017 Trail of Bits, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <glog/logging.h>
#include <gflags/gflags.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/DebugInfo.h>
#include <llvm/IR/InstIterator.h>
#include <llvm/IR/InlineAsm.h>
#include <llvm/IR/Intrinsics.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Metadata.h>
#include <llvm/IR/MDBuilder.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/PassManager.h>
#include <llvm/IR/Type.h>
#include <llvm/Transforms/Scalar.h>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "remill/Arch/Arch.h"
#include "remill/Arch/Instruction.h"
#include "remill/BC/ABI.h"
#include "remill/BC/IntrinsicTable.h"
#include "remill/BC/Lifter.h"
#include "remill/BC/Util.h"
#include "remill/BC/Version.h"
#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/Callback.h"
#include "mcsema/BC/Function.h"
#include "mcsema/BC/Instruction.h"
#include "mcsema/BC/Legacy.h"
#include "mcsema/BC/Lift.h"
#include "mcsema/BC/Optimize.h"
#include "mcsema/BC/Segment.h"
#include "mcsema/BC/Util.h"
#include "mcsema/CFG/CFG.h"
DECLARE_bool(legacy_mode);
DEFINE_bool(add_reg_tracer, false,
"Add a debug function that prints out the register state before "
"each lifted instruction execution.");
DEFINE_bool(add_breakpoints, false,
"Add 'breakpoint' functions between every lifted instruction. This "
"allows one to set a breakpoint, in the lifted code, just before a "
"specific lifted instruction is executed. This is a debugging aid.");
DEFINE_bool(check_pc_at_breakpoints, false,
"Check whether or not the emulated program counter is correct at "
"each injected 'breakpoint' function. This is a debugging aid.");
namespace mcsema {
namespace {
static llvm::Function *GetRegTracer(void) {
auto reg_tracer = gModule->getFunction("__mcsema_reg_tracer");
if (!reg_tracer) {
reg_tracer = llvm::Function::Create(
LiftedFunctionType(), llvm::GlobalValue::ExternalLinkage,
"__mcsema_reg_tracer", gModule);
}
return reg_tracer;
}
static llvm::Function *GetBreakPoint(uint64_t pc) {
std::stringstream ss;
ss << "breakpoint_" << std::hex << pc;
auto func_name = ss.str();
auto func = gModule->getFunction(func_name);
if (func) {
return func;
}
func = llvm::Function::Create(
LiftedFunctionType(), llvm::GlobalValue::ExternalLinkage,
func_name, gModule);
// Make sure to keep this function around (along with `ExternalLinkage`).
func->addFnAttr(llvm::Attribute::OptimizeNone);
func->addFnAttr(llvm::Attribute::NoInline);
func->removeFnAttr(llvm::Attribute::ReadNone);
#if LLVM_VERSION_NUMBER < LLVM_VERSION(3, 7)
func->addFnAttr(llvm::Attribute::ReadOnly);
#else
func->addFnAttr(llvm::Attribute::ArgMemOnly);
#endif
auto state_ptr = remill::NthArgument(func, remill::kStatePointerArgNum);
auto state_ptr_type = state_ptr->getType();
llvm::IRBuilder<> ir(llvm::BasicBlock::Create(*gContext, "", func));
if (FLAGS_check_pc_at_breakpoints) {
auto trap = llvm::Intrinsic::getDeclaration(gModule, llvm::Intrinsic::trap);
auto are_eq = ir.CreateICmpEQ(
remill::NthArgument(func, remill::kPCArgNum),
llvm::ConstantInt::get(gWordType, pc));
auto not_eq_bb = llvm::BasicBlock::Create(*gContext, "", func);
auto eq_bb = llvm::BasicBlock::Create(*gContext, "", func);
ir.CreateCondBr(are_eq, eq_bb, not_eq_bb);
ir.SetInsertPoint(not_eq_bb);
ir.CreateCall(trap);
ir.CreateUnreachable();
ir.SetInsertPoint(eq_bb);
}
// Basically some empty inline assembly that tells the compiler not to
// optimize away the `state` pointer before each `breakpoint_XXX` function.
auto asm_func_type = llvm::FunctionType::get(
llvm::Type::getVoidTy(*gContext), state_ptr_type);
auto asm_func = llvm::InlineAsm::get(
asm_func_type, "", "*m,~{dirflag},~{fpsr},~{flags}", true);
ir.CreateCall(asm_func, state_ptr);
ir.CreateRet(remill::NthArgument(func, remill::kMemoryPointerArgNum));
return func;
}
// Tries to get the lifted function beginning at `pc`.
static llvm::Function *GetLiftedFunction(const NativeModule *cfg_module,
uint64_t pc) {
if (auto cfg_func = cfg_module->TryGetFunction(pc)) {
return gModule->getFunction(cfg_func->lifted_name);
}
return nullptr;
}
// Add a call to another function, and then update the memory pointer with the
// result of the function.
static void InlineSubFuncCall(llvm::BasicBlock *block,
llvm::Function *sub) {
auto call = llvm::CallInst::Create(
sub, remill::LiftedFunctionArgs(block), "", block);
call->setCallingConv(sub->getCallingConv());
auto mem_ptr = remill::LoadMemoryPointerRef(block);
(void) new llvm::StoreInst(call, mem_ptr, block);
}
// Find an external function associated with this indirect jump.
static llvm::Function *DevirtualizeIndirectFlow(
TranslationContext &ctx, llvm::Function *fallback) {
if (ctx.cfg_inst->flow) {
if (auto cfg_func = ctx.cfg_inst->flow->func) {
if (cfg_func->is_external) {
return GetLiftedToNativeExitPoint(cfg_func);
} else {
return gModule->getFunction(cfg_func->lifted_name);
}
}
}
return fallback;
}
// Try to find a function. We start by assuming that `target_pc` is an
// absolute address for the function. This is usually the case for direct
// function calls internal to a binary. However, if the function is actually
// an external function then we try to return the external version of the
// function.
static llvm::Function *FindFunction(TranslationContext &ctx,
uint64_t target_pc) {
const NativeFunction *cfg_func = nullptr;
if (ctx.cfg_inst->flow) {
cfg_func = ctx.cfg_inst->flow->func;
if (cfg_func && cfg_func->is_external) {
return GetLiftedToNativeExitPoint(cfg_func);
}
}
auto func = GetLiftedFunction(ctx.cfg_module, target_pc);
if (func) {
return func;
}
if (cfg_func) {
auto lifted_func = gModule->getFunction(cfg_func->lifted_name);
if (lifted_func) {
LOG(WARNING)
<< "Cannot find target of instruction at " << std::hex
<< ctx.cfg_inst->ea << "; the static target "
<< std::hex << target_pc << " is not associated with a lifted"
<< " subroutine, but is associated with the function at " << std::hex
<< cfg_func->ea << " in the CFG." << std::dec;
return lifted_func;
}
}
LOG(ERROR)
<< "Cannot find target of instruction at " << std::hex
<< ctx.cfg_inst->ea << "; the static target "
<< std::hex << target_pc << " is not associated with a lifted"
<< " subroutine, and it does not have a known call target."
<< std::dec;
return ctx.lifter->intrinsics->error;
}
// Get the basic block within this function associated with a specific program
// counter.
static llvm::BasicBlock *GetOrCreateBlock(TranslationContext &ctx,
uint64_t pc) {
auto &block = ctx.ea_to_block[pc];
if (!block) {
std::stringstream ss;
ss << "block_" << std::hex << pc;
block = llvm::BasicBlock::Create(*gContext, ss.str(), ctx.lifted_func);
// First, try to see if it's actually related to another function. This is
// equivalent to a tail-call in the original code.
if (auto tail_called_func = FindFunction(ctx, pc)) {
LOG_IF(ERROR, !ctx.cfg_block->successor_eas.count(pc))
<< "Adding missing block " << std::hex << pc << " in function "
<< ctx.cfg_func->lifted_name << " as a tail call to "
<< tail_called_func->getName().str() << std::dec;
remill::AddTerminatingTailCall(block, tail_called_func);
// Terminate the block with an unreachable inst.
} else {
LOG(ERROR)
<< "Adding missing block " << std::hex << pc << " in function "
<< ctx.cfg_func->lifted_name << " as a jump to the error intrinsic."
<< std::dec;
remill::AddTerminatingTailCall(
block, ctx.lifter->intrinsics->missing_block);
}
}
return block;
}
// Lift both targets of a conditional branch into a branch in the bitcode,
// where each side of the branch tail-calls to the functions associated with
// the lifted blocks for those branch targets.
static void LiftConditionalBranch(TranslationContext &ctx,
llvm::BasicBlock *source,
remill::Instruction &inst) {
auto block_true = GetOrCreateBlock(ctx, inst.branch_taken_pc);
auto block_false = GetOrCreateBlock(ctx, inst.branch_not_taken_pc);
llvm::IRBuilder<> cond_ir(source);
cond_ir.CreateCondBr(
remill::LoadBranchTaken(source),
block_true,
block_false);
}
// Lift an indirect jump into a switch instruction.
static void LiftIndirectJump(TranslationContext &ctx,
llvm::BasicBlock *block,
remill::Instruction &inst) {
auto exit_point = GetLiftedToNativeExitPoint(kExitPointJump);
auto fallback = DevirtualizeIndirectFlow(ctx, exit_point);
std::unordered_map<uint64_t, llvm::BasicBlock *> block_map;
for (auto target_ea : ctx.cfg_block->successor_eas) {
block_map[target_ea] = GetOrCreateBlock(ctx, target_ea);
fallback = ctx.lifter->intrinsics->missing_block;
}
if (exit_point == fallback) {
// If we have no targets, then a reasonable target turns out to be the next
// program counter.
if (block_map.empty()) {
block_map[inst.next_pc] = GetOrCreateBlock(ctx, inst.next_pc);
}
// Build up a set of all reachable blocks that were known at disassembly
// time so that we can find blocks that have no predecessors.
std::unordered_set<uint64_t> succ_eas;
succ_eas.insert(ctx.cfg_func->ea);
for (auto block_entry : ctx.cfg_func->blocks) {
auto cfg_block = block_entry.second;
succ_eas.insert(cfg_block->successor_eas.begin(),
cfg_block->successor_eas.end());
}
// We'll augment our block map to also target unreachable blocks, just in
// case our disassembly failed to find some of the targets.
for (auto block_entry : ctx.cfg_func->blocks) {
auto target_ea = block_entry.first;
if (!succ_eas.count(target_ea)) {
LOG(WARNING)
<< "Adding block " << std::hex << target_ea
<< " with no predecessors as additional target of the "
<< " indirect jump at " << inst.pc << std::dec;
block_map[target_ea] = GetOrCreateBlock(ctx, target_ea);
}
}
}
// We have no jump table information, so assume that it's an indirect tail
// call, so just go native.
if (block_map.empty()) {
LOG(INFO)
<< "Indirect jump at " << std::hex << inst.pc << std::dec
<< " looks like a thunk; falling back to " << fallback->getName().str();
remill::AddTerminatingTailCall(block, fallback);
return;
}
// Create a "default" fall-back block for the switch.
auto fallback_block = llvm::BasicBlock::Create(
*gContext, "", block->getParent());
remill::AddTerminatingTailCall(fallback_block, fallback);
auto num_blocks = static_cast<unsigned>(block_map.size());
auto switch_index = remill::LoadProgramCounter(block);
if (ctx.cfg_inst->offset_table) {
LOG(INFO)
<< "Indirect jump at " << std::hex << ctx.cfg_inst->ea
<< " is a jump through an offset table with offset "
<< std::hex << ctx.cfg_inst->offset_table->target_ea << std::dec;
llvm::IRBuilder<> ir(block);
switch_index = ir.CreateAdd(
ir.CreateSub(switch_index,
LiftEA(ctx.cfg_inst->offset_table->target_segment,
ctx.cfg_inst->offset_table->target_ea)),
llvm::ConstantInt::get(
gWordType, ctx.cfg_inst->offset_table->target_ea));
remill::StoreProgramCounter(block, switch_index);
}
auto switch_inst = llvm::SwitchInst::Create(
switch_index, fallback_block, num_blocks, block);
LOG(INFO)
<< "Indirect jump at " << std::hex << inst.pc
<< " has " << std::dec << num_blocks << " targets";
// Add the cases.
for (auto ea_to_block : block_map) {
switch_inst->addCase(
llvm::ConstantInt::get(ctx.lifter->word_type, ea_to_block.first, false),
ea_to_block.second);
}
}
// Returns `true` if `instr` should end a basic block and if a terminator was
// added to end the block.
static bool TryLiftTerminator(TranslationContext &ctx,
llvm::BasicBlock *block,
remill::Instruction &inst) {
switch (inst.category) {
case remill::Instruction::kCategoryInvalid:
case remill::Instruction::kCategoryError:
remill::AddTerminatingTailCall(block, ctx.lifter->intrinsics->error);
return true;
case remill::Instruction::kCategoryNormal:
case remill::Instruction::kCategoryNoOp:
return false;
case remill::Instruction::kCategoryDirectJump:
llvm::BranchInst::Create(
GetOrCreateBlock(ctx, inst.branch_taken_pc), block);
return true;
case remill::Instruction::kCategoryIndirectJump:
LiftIndirectJump(ctx, block, inst);
return true;
case remill::Instruction::kCategoryDirectFunctionCall:
// Treat a `call +5` as not actually needing to call out to a
// new subroutine.
if (inst.branch_taken_pc != inst.next_pc) {
auto targ_func = FindFunction(ctx, inst.branch_taken_pc);
DLOG(INFO)
<< "Function " << ctx.lifted_func->getName().str()
<< " calls " << targ_func->getName().str()
<< " at " << std::hex << inst.pc << std::dec;
InlineSubFuncCall(block, targ_func);
} else {
LOG(WARNING)
<< "Not adding a subroutine self-call at "
<< std::hex << inst.pc << std::dec;
}
return false;
case remill::Instruction::kCategoryIndirectFunctionCall:
InlineSubFuncCall(
block,
DevirtualizeIndirectFlow(
ctx, GetLiftedToNativeExitPoint(kExitPointFunctionCall)));
return false;
case remill::Instruction::kCategoryFunctionReturn:
llvm::ReturnInst::Create(
*gContext, remill::LoadMemoryPointer(block), block);
return true;
case remill::Instruction::kCategoryConditionalBranch:
case remill::Instruction::kCategoryConditionalAsyncHyperCall:
LiftConditionalBranch(ctx, block, inst);
return true;
case remill::Instruction::kCategoryAsyncHyperCall:
InlineSubFuncCall(block, ctx.lifter->intrinsics->async_hyper_call);
remill::StoreProgramCounter(block, inst.next_pc);
return false;
}
return false;
}
// Lift a decoded instruction into `block`.
static bool LiftInstIntoBlock(TranslationContext &ctx,
llvm::BasicBlock *block,
bool is_last) {
remill::Instruction inst;
auto inst_addr = ctx.cfg_inst->ea;
auto &bytes = ctx.cfg_inst->bytes;
if (FLAGS_legacy_mode) {
remill::StoreProgramCounter(block, inst_addr);
}
if (!gArch->DecodeInstruction(inst_addr, bytes, inst)) {
LOG(ERROR)
<< "Unable to decode instruction " << inst.Serialize()
<< " at " << std::hex << inst_addr << std::dec;
remill::AddTerminatingTailCall(block, ctx.lifter->intrinsics->error);
return false;
}
DLOG_IF(WARNING, bytes.size() != inst.NumBytes())
<< "Size of decoded instruction at " << std::hex << inst_addr
<< " (" << std::dec << inst.NumBytes()
<< ") doesn't match input instruction size ("
<< bytes.size() << ").";
if (FLAGS_add_reg_tracer) {
InlineSubFuncCall(block, GetRegTracer());
}
if (FLAGS_add_breakpoints) {
InlineSubFuncCall(block, GetBreakPoint(inst_addr));
}
ctx.lifter->LiftIntoBlock(inst, block);
auto ret = true;
if (TryLiftTerminator(ctx, block, inst)) {
if (!is_last) {
LOG(ERROR)
<< "Ending block early at " << std::hex << inst_addr;
ret = false;
}
}
// Annotate every un-annotated instruction in this function with the
// program counter of the current instruction.
if (FLAGS_legacy_mode) {
legacy::AnnotateInsts(ctx.lifted_func, inst_addr);
}
return ret;
}
// Lift a decoded block into a function.
static void LiftBlockIntoFunction(TranslationContext &ctx) {
auto block_name = ctx.cfg_block->lifted_name;
auto block_pc = ctx.cfg_block->ea;
auto block = ctx.ea_to_block[block_pc];
ctx.cfg_inst = nullptr;
// Lift each instruction into the block.
size_t i = 0;
const auto num_insts = ctx.cfg_block->instructions.size();
for (auto cfg_inst : ctx.cfg_block->instructions) {
ctx.cfg_inst = cfg_inst;
auto is_last = (++i) >= num_insts;
if (!LiftInstIntoBlock(ctx, block, is_last)) {
return;
}
LOG_IF(WARNING, cfg_inst->does_not_return && !is_last)
<< "Instruction at " << std::hex << cfg_inst->ea
<< " has a local no-return, but is not the last instruction"
<< " in its block (" << std::hex << block_pc
<< "). Terminating anyway." << std::dec;
}
const auto &follows = ctx.cfg_block->successor_eas;
if (!block->getTerminator()) {
if (!ctx.cfg_inst || ctx.cfg_inst->does_not_return) {
LOG_IF(ERROR, !ctx.cfg_inst)
<< "Block " << std::hex << block_pc << " in function "
<< ctx.cfg_func->ea << std::dec << " has no instructions; "
<< "this could be because of an incorrectly disassembled jump table.";
remill::AddTerminatingTailCall(block, ctx.lifter->intrinsics->error);
} else if (follows.size() == 1) {
(void) llvm::BranchInst::Create(
GetOrCreateBlock(ctx, *(follows.begin())), block);
} else {
LOG(ERROR)
<< "Block " << std::hex << block_pc << " has no terminator, and"
<< " instruction at " << std::hex << ctx.cfg_inst->ea
<< " is not a local no-return function call." << std::dec;
remill::AddTerminatingTailCall(
block, ctx.lifter->intrinsics->missing_block);
}
}
}
// Allocate storage of the stack variables that we're going to lift.
static void AllocStackVars(llvm::BasicBlock *bb,
const NativeFunction *cfg_func) {
llvm::IRBuilder<> ir(bb);
llvm::Value *array_size = nullptr;
llvm::Type *array_type = nullptr;
for (auto s : cfg_func->stack_vars) {
switch(s->size){
case 1:
case 2:
case 4:
case 8:
array_type = llvm::Type::getIntNTy(
*gContext, static_cast<unsigned>(s->size * 8U));
array_size = llvm::ConstantInt::get(gWordType, 1);
break;
default:
array_type = llvm::Type::getInt8Ty(*gContext);
array_size = llvm::ConstantInt::get(gWordType, s->size);
break;
}
LOG(INFO)
<< "Inserting " << s->size << "-byte variable " << s->name
<< " into function" << cfg_func->name;
// TODO(kumarak): Alignment of `alloca`s?
s->llvm_var = ir.CreateAlloca(array_type, array_size, s->name);
}
}
static llvm::Function *LiftFunction(
const NativeModule *cfg_module, const NativeFunction *cfg_func) {
CHECK(!cfg_func->is_external)
<< "Should not lift external function " << cfg_func->name;
static std::unique_ptr<remill::IntrinsicTable> intrinsics;
if (!intrinsics.get()) {
intrinsics.reset(new remill::IntrinsicTable(gModule));
}
auto lifted_func = gModule->getFunction(cfg_func->lifted_name);
CHECK(nullptr != lifted_func)
<< "Could not find declaration for " << cfg_func->lifted_name;
// This can happen due to deduplication of functions during the
// CFG decoding process. In practice, though, that only really
// affects externals.
if (!lifted_func->empty()) {
LOG(WARNING)
<< "Asking to re-insert function: " << cfg_func->lifted_name
<< "; returning current function instead";
return lifted_func;
}
if (cfg_func->blocks.empty()) {
LOG(ERROR)
<< "Function " << cfg_func->lifted_name << " is empty!";
remill::AddTerminatingTailCall(lifted_func, intrinsics->missing_block);
return lifted_func;
}
remill::CloneBlockFunctionInto(lifted_func);
lifted_func->removeFnAttr(llvm::Attribute::AlwaysInline);
lifted_func->removeFnAttr(llvm::Attribute::InlineHint);
lifted_func->removeFnAttr(llvm::Attribute::NoReturn);
lifted_func->addFnAttr(llvm::Attribute::NoInline);
lifted_func->setVisibility(llvm::GlobalValue::DefaultVisibility);
TranslationContext ctx;
std::unique_ptr<remill::InstructionLifter> lifter(
new InstructionLifter(intrinsics.get(), ctx));
ctx.lifter = lifter.get();
ctx.cfg_module = cfg_module;
ctx.cfg_func = cfg_func;
ctx.cfg_block = nullptr;
ctx.cfg_inst = nullptr;
ctx.lifted_func = lifted_func;
std::unordered_set<uint64_t> referenced_blocks;
referenced_blocks.insert(cfg_func->ea);
// Create basic blocks for each basic block in the original function.
for (auto block_info : cfg_func->blocks) {
auto cfg_block = block_info.second;
ctx.ea_to_block[block_info.first] = llvm::BasicBlock::Create(
*gContext, cfg_block->lifted_name, lifted_func);
}
// Allocate the stack variable recovered in the function
auto entry_block = ctx.ea_to_block[cfg_func->ea];
AllocStackVars(entry_block, cfg_func);
llvm::BranchInst::Create(ctx.ea_to_block[cfg_func->ea],
&(lifted_func->front()));
for (auto block_info : cfg_func->blocks) {
ctx.cfg_block = block_info.second;
LiftBlockIntoFunction(ctx);
}
// Check the sanity of things.
for (auto block_info : ctx.ea_to_block) {
auto block = block_info.second;
CHECK(block->getTerminator() != nullptr)
<< "Lifted block " << std::hex << block_info.first
<< " has no terminator!" << std::dec;
}
return lifted_func;
}
} // namespace
// Declare the lifted functions. This is a separate step from defining
// functions because it's important that all possible code- and data-cross
// references are resolved before any data or instructions can use
// those references.
void DeclareLiftedFunctions(const NativeModule *cfg_module) {
for (auto func : cfg_module->ea_to_func) {
auto cfg_func = func.second->Get();
if (cfg_func->is_external) {
continue;
}
const auto &func_name = cfg_func->lifted_name;
auto lifted_func = gModule->getFunction(func_name);
if (!lifted_func) {
lifted_func = remill::DeclareLiftedFunction(gModule, func_name);
// make local functions 'static'
LOG(INFO)
<< "Inserted function: " << func_name;
} else {
LOG(INFO)
<< "Already inserted function: " << func_name << ", skipping.";
}
}
}
// Lift the blocks and instructions into the function. Some minor optimization
// passes are applied to clean out any unneeded register variables brought
// in from cloning the `__remill_basic_block` function.
bool DefineLiftedFunctions(const NativeModule *cfg_module) {
llvm::legacy::FunctionPassManager func_pass_manager(gModule);
func_pass_manager.add(llvm::createCFGSimplificationPass());
func_pass_manager.add(llvm::createPromoteMemoryToRegisterPass());
func_pass_manager.add(llvm::createReassociatePass());
func_pass_manager.add(llvm::createDeadStoreEliminationPass());
func_pass_manager.add(llvm::createDeadCodeEliminationPass());
func_pass_manager.doInitialization();
for (auto &entry : cfg_module->ea_to_func) {
auto cfg_func = reinterpret_cast<const NativeFunction *>(
entry.second->Get());
if (cfg_func->is_external) {
continue;
}
auto lifted_func = LiftFunction(cfg_module, cfg_func);
if (!lifted_func) {
LOG(ERROR)
<< "Could lift function: " << cfg_func->name << " at "
<< std::hex << cfg_func->ea << " into " << cfg_func->lifted_name
<< std::dec;
return false;
}
func_pass_manager.run(*lifted_func);
}
func_pass_manager.doFinalization();
return true;
}
} // namespace mcsema