Files
lifting-bits-mcsema/mcsema/BC/Segment.cpp
T
Peter Goodman 2d55d02cc3 API improvements. Must be used with the api_improvements branches of Remill and Anvill (#666)
* API improvements. Must be used with the api_improvements branch of both Remill and McSema

fixes for x86 and running the lifted code with klee

* Update dockerfile to clone anvill

* update remill commit id

* Add python3 to dockerfile

* update python3

* disable abi script

* Updated cmake to find anvill

* Update main.cpp

* update find_package for anvill

* WIP:updated prebuild cfg

* update prebuild cfg files

* enable abi build for testsuite

* Fix memory leak

* install missing package for testcases

* frontend: Reflect cfg file changes in dyninst frontend.

* frontend: Update local files copyrights to reflect overall change to agplv3.

* fix failing testcases

* update test cfgs

* Fix test failure with local state pointer

* set the flag to use local state_ptr in default mode

Co-authored-by: kumarak <iit.akshay@gmail.com>
Co-authored-by: Lukas Korencik <xkorenc1@fi.muni.cz>
2020-07-23 02:14:56 -04:00

724 lines
24 KiB
C++

/*
* Copyright (c) 2020 Trail of Bits, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mcsema/BC/Segment.h"
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <algorithm>
#include <iomanip>
#include <limits>
#include <sstream>
#include <string>
#include <vector>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>
#include <remill/Arch/Arch.h>
#include <remill/BC/Compat/GlobalValue.h>
#include <remill/BC/Util.h>
#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/Callback.h"
#include "mcsema/BC/Util.h"
#include "mcsema/CFG/CFG.h"
DEFINE_string(libc_constructor, "",
"Constructor function for running pre-`main` initializers. This "
"is a (lifted) function that will be executed before the `main` "
"function is executed. This feature should be used when lifting "
"code compiled from C++ programs. Many C++ programs will "
"construct global objects before the `main` function is executed,"
" and those constructors will be called via a function like "
"`__libc_csu_init` (on GNU-based systems).");
DEFINE_string(libc_destructor, "",
"Destructor function for running post-`main` finalizers. This "
"is a (lifted) function that will be executed after the `main` "
"function returns. For example, on GNU-based systems, this is "
"typically `__libc_csu_fini`.");
DEFINE_bool(force_embed_data_refs, false,
"Should data-to-code and data-to-data cross-references be force-"
"embedded within data segments? This is a good option to enable "
"when using McSema-produced bitcode in KLEE, as it avoids doing "
"lazy cross-reference initialization.");
DECLARE_bool(disable_aliases);
namespace mcsema {
namespace {
// Lets us decide if we can use a constant aggregate zero value, i.e.
// direct LLVM to put this data into the `.bss` segment.
static bool IsZero(const NativeSegment *cfg_seg) {
for (const auto &cfg_seg_entry : cfg_seg->entries) {
const auto &entry = cfg_seg_entry.second;
if (!entry.blob || !entry.blob->is_zero) {
return false;
}
}
return true;
}
// The type of the segment is a packed structure that is a sequence of opaque
// byte arrays, interspersed with cross-references, which will be represented
// as (pointers casted to) integers.
static llvm::StructType *GetSegmentType(const NativeSegment *cfg_seg) {
std::stringstream ss;
ss << cfg_seg->lifted_name << "_type";
const auto type_name = ss.str();
auto seg_type = gModule->getTypeByName(type_name);
if (seg_type) {
return seg_type;
}
std::vector<llvm::Type *> entry_types;
auto byte_type = llvm::Type::getInt8Ty(*gContext);
if (cfg_seg->padding) {
auto arr_type = llvm::ArrayType::get(byte_type, cfg_seg->padding);
entry_types.push_back(arr_type);
}
for (const auto &cfg_seg_entry : cfg_seg->entries) {
const auto &entry = cfg_seg_entry.second;
if (entry.blob) {
auto arr_type = llvm::ArrayType::get(byte_type, entry.blob->size);
entry_types.push_back(arr_type);
} else if (entry.xref) {
const auto entry_size = entry.xref->width * 8;
if (entry_size == gArch->address_size) {
entry_types.push_back(llvm::Type::getInt8PtrTy(*gContext, 0));
} else {
entry_types.push_back(llvm::Type::getIntNTy(
*gContext, static_cast<unsigned>(entry_size)));
}
} else {
LOG(FATAL)
<< "Missing blob or xref segment entry value for segment "
<< cfg_seg->name << " data at " << std::hex << entry.ea;
}
}
seg_type = llvm::StructType::create(entry_types, type_name, true);
CHECK(nullptr != seg_type)
<< "Unable to create structure type for segment " << cfg_seg->name
<< " beginning at " << std::hex << cfg_seg->ea;
llvm::DataLayout data_layout(gModule.get());
auto seg_size = data_layout.getTypeAllocSize(seg_type);
CHECK_EQ(seg_size, (cfg_seg->size + cfg_seg->padding))
<< "Size of structure type of segment " << cfg_seg->name
<< " is " << seg_size << " but was expected to be " << cfg_seg->size;
return seg_type;
}
static llvm::GlobalValue::ThreadLocalMode ThreadLocalMode(
const NativeObject *cfg_obj) {
if (cfg_obj->is_thread_local) {
return llvm::GlobalValue::InitialExecTLSModel;
} else {
return llvm::GlobalValue::NotThreadLocal;
}
}
// Create a McSema-specific constructor/destructor function, and add it to the
// corresponding LLVM-specific array.
static llvm::Function *CreateMcSemaInitFiniImpl(
const char *func_name, const char *arr_name) {
LOG(INFO)
<< "Creating " << func_name << " function to initialize runtime.";
auto func = llvm::Function::Create(
llvm::FunctionType::get(llvm::Type::getVoidTy(*gContext), false),
llvm::GlobalValue::InternalLinkage,
func_name, gModule.get());
auto bool_type = llvm::Type::getInt1Ty(*gContext);
auto check_var = new llvm::GlobalVariable(
bool_type, false, llvm::GlobalValue::InternalLinkage);
check_var->setInitializer(llvm::Constant::getNullValue(bool_type));
auto entry = llvm::BasicBlock::Create(*gContext, "", func);
llvm::IRBuilder<> ir(entry);
ir.CreateRetVoid();
auto i32_type = llvm::Type::getInt32Ty(*gContext);
auto ptr_type = llvm::Type::getInt8PtrTy(*gContext);
// Type of an entry in the init/fini array.
std::vector<llvm::Type *> element_types;
element_types.push_back(i32_type);
element_types.push_back(func->getType());
element_types.push_back(ptr_type);
auto el_type = llvm::StructType::get(*gContext, element_types);
// Init/fini array entry.
std::vector<llvm::Constant *> element_inits;
element_inits.push_back(llvm::ConstantInt::get(i32_type, 101));
element_inits.push_back(func);
element_inits.push_back(llvm::Constant::getNullValue(ptr_type));
auto el_init = llvm::ConstantStruct::get(el_type, element_inits);
std::vector<llvm::Constant *> new_elems;
auto global_ctors = gModule->getGlobalVariable(arr_name);
if (global_ctors) {
LOG(INFO)
<< "Module already has a " << arr_name << " array.";
auto arr = llvm::dyn_cast<llvm::ConstantArray>(
global_ctors->getInitializer());
auto num_ops = arr->getNumOperands();
for (auto i = 0U; i < num_ops; ++i) {
new_elems.push_back(arr->getOperand(i));
}
}
new_elems.push_back(el_init);
auto arr_type = llvm::ArrayType::get(el_type, new_elems.size());
auto arr_init = llvm::ConstantArray::get(arr_type, new_elems);
auto arr = new llvm::GlobalVariable(
*gModule, arr_type, false /* isConstant */,
llvm::GlobalVariable::AppendingLinkage,
arr_init, global_ctors ? "__mcsema.temp_array" : arr_name);
if (global_ctors) {
arr->takeName(global_ctors);
global_ctors->dropAllReferences();
global_ctors->eraseFromParent();
}
return func;
}
static llvm::Function *GetOrCreateMcSemaConstructor(void) {
static llvm::Function *gInitFunc = nullptr;
if (gInitFunc) {
return gInitFunc;
}
gInitFunc = CreateMcSemaInitFiniImpl(
"__mcsema_constructor", "llvm.global_ctors");
return gInitFunc;
}
static llvm::Function *GetOrCreateMcSemaDestructor(void) {
static llvm::Function *gFiniFunc = nullptr;
if (!gFiniFunc) {
gFiniFunc = CreateMcSemaInitFiniImpl(
"__mcsema_destructor", "llvm.global_dtors");
}
return gFiniFunc;
}
// Add code to the `__mcsema_constructor` function to lazily (i.e. at runtime)
// initialize the value of a cross-reference.
//
// Consider the following C code:
//
// FILE *gFilePtr = stdout;
//
// The type of `gFilePtr` is a `FILE **`, and it will show up as such in
// the bitcode. `stdout` is an extern, and its initializer is not present.
// What a compiler does in this case is allocate space for `gFilePtr` in the
// `.bss` section, and produces some actual code that runs as a constructor
// function that initializes the actual pointer at runtime before `main` is
// executed.
//
// Sometimes IDA picks up on these types of references, and so we need to
// handle them by emulating this runtime initialization. We create a special
// constructor function of our own, `__mcsema_constructor`, that will contain
// code that can do these initializations.
static void LazyInitXRef(const NativeXref *xref,
llvm::Constant *target_addr_const) {
auto init_func = GetOrCreateMcSemaInitializer();
llvm::BasicBlock *block = &init_func->back();
llvm::IRBuilder<> ir(block);
ir.SetInsertPoint(&block->front());
if (xref->segment->is_external) {
LOG(ERROR)
<< "Ignoring lazy initialization of cross-reference to "
<< std::hex << xref->target_ea << " at " << xref->ea
<< " in external segment '" << xref->segment->name << "' at "
<< xref->segment->ea << std::dec;
return;
}
auto seg = llvm::dyn_cast<llvm::GlobalVariable>(xref->segment->Pointer());
if (seg->isConstant()) {
LOG(WARNING)
<< "Marking " << seg->getName().str() << " as non-constant to "
<< "support lazy initialization of reference to " << std::hex
<< xref->target_ea << " from " << xref->ea << std::dec;
seg->setConstant(false);
}
if (target_addr_const->isNullValue()) {
return;
}
llvm::Value *target_addr = target_addr_const;
switch (xref->fixup_kind) {
case NativeXref::kAbsoluteFixup: {
CHECK(!xref->var || !xref->var->is_thread_local)
<< "Cannot do absolute fixup from " << std::hex << xref->ea
<< " to thread-local variable at "
<< std::hex << xref->target_ea << std::dec;
// `target_addr` already has the right value.
break;
}
case NativeXref::kThreadLocalOffsetFixup: {
CHECK(xref->var != nullptr)
<< "Non-variable thread-local cross-reference from "
<< std::hex << xref->ea << " to " << xref->target_ea << std::dec;
CHECK(xref->var->is_thread_local)
<< "Cannot do thread-local fixup from " << std::hex << xref->ea
<< " to non-thread-local variable at "
<< std::hex << xref->target_ea << std::dec;
static llvm::Value *thread_base = nullptr;
if (!thread_base) {
thread_base = GetTLSBaseAddress(ir);
}
target_addr = ir.CreateSub(target_addr, thread_base);
break;
}
}
auto addr_of_xref = LiftXrefInData(xref->segment, xref->ea, false);
ir.CreateStore(target_addr, addr_of_xref);
}
// Fill in the contents of the data segment.
static llvm::Constant *FillDataSegment(const NativeModule *cfg_module,
const NativeSegment *cfg_seg,
llvm::StructType *seg_type) {
if (IsZero(cfg_seg)) {
return llvm::ConstantAggregateZero::get(seg_type);
}
std::vector<llvm::Constant *> entry_vals;
// We might add some padding on the beginning of this
unsigned i = 0;
if (cfg_seg->padding) {
auto byte_type = llvm::Type::getInt8Ty(*gContext);
auto arr_type = llvm::ArrayType::get(byte_type, cfg_seg->padding);
entry_vals.push_back(llvm::ConstantAggregateZero::get(arr_type));
++i;
}
for (auto &cfg_seg_entry : cfg_seg->entries) {
auto &entry = cfg_seg_entry.second;
auto entry_type = seg_type->getContainedType(i++);
// This entry is an opaque sequence of bytes.
if (entry.blob) {
if (entry.blob->is_zero) {
entry_vals.push_back(llvm::ConstantAggregateZero::get(entry_type));
} else if (auto bytes = cfg_module->FindBytes(entry.blob->ea, entry.blob->size);
bytes && bytes.Size() == entry.blob->size) {
const llvm::StringRef str_data(bytes.ToString().data(), bytes.Size());
auto data = llvm::ConstantDataArray::getString(
*gContext, str_data, false /* AddNull */);
CHECK(data->getType() == entry_type)
<< "Type mismatch: Got "
<< remill::LLVMThingToString(data->getType())
<< " but expected "
<< remill::LLVMThingToString(entry_type);
entry_vals.push_back(data);
} else {
LOG(ERROR)
<< "Could not find data backing " << std::hex << entry.blob->ea
<< std::dec << " in segment " << cfg_seg->name;
entry_vals.push_back(llvm::ConstantAggregateZero::get(entry_type));
}
// This entry is a cross-reference.
} else if (entry.xref) {
auto &xref = entry.xref;
llvm::Constant *val = nullptr;
CHECK((entry.xref->width * 8) <= gArch->address_size)
<< "Cross-reference at " << std::hex
<< xref->ea << " to " << std::hex << xref->target_ea
<< " is too wide at " << entry.xref->width << " bytes";
auto be_lazy = false;
// Pointer to a (possibly external) function.
if (xref->func) {
const auto cfg_func = xref->func->Get();
val = cfg_func->Pointer();
// Pointer to a global (possibly external) variable.
} else if (xref->var) {
const auto cfg_var = xref->var->Get();
val = cfg_var->Pointer();
be_lazy = cfg_var->is_external || cfg_var->is_thread_local;
// Pointer to an unnamed location inside of a data segment.
} else if (xref->target_segment) {
const auto target_seg = xref->target_segment;
val = LiftXrefInData(target_seg, xref->target_ea, false);
be_lazy = target_seg->is_external || target_seg->is_thread_local;
} else {
val = llvm::ConstantInt::get(gWordType, xref->target_ea);
if (entry_type->isPointerTy()) {
val = llvm::ConstantExpr::getIntToPtr(val, entry_type);
} else {
val = llvm::ConstantExpr::getIntToPtr(
val, llvm::Type::getInt8PtrTy(*gContext));
}
}
auto val_type = llvm::dyn_cast<llvm::PointerType>(val->getType());
CHECK_NOTNULL(val_type);
if (val_type->getAddressSpace()) {
val_type = llvm::PointerType::get(val_type->getElementType(), 0);
val = llvm::ConstantExpr::getAddrSpaceCast(val, val_type);
}
if (entry_type->isIntegerTy()) {
val = llvm::ConstantExpr::getPtrToInt(val, gWordType);
if ((gArch->address_size / 8) > xref->width) {
val = llvm::ConstantExpr::getTrunc(val, entry_type);
be_lazy = true;
}
} else if (val_type != entry_type) {
CHECK(entry_type->isPointerTy());
val = llvm::ConstantExpr::getBitCast(val, entry_type);
}
if (be_lazy && !FLAGS_force_embed_data_refs) {
LazyInitXRef(xref.get(), val);
val = llvm::Constant::getNullValue(entry_type);
}
CHECK_EQ(val->getType(), entry_type)
<< "Type mismatch: Got "
<< remill::LLVMThingToString(val->getType())
<< " but expected "
<< remill::LLVMThingToString(entry_type);
entry_vals.push_back(val);
} else {
LOG(FATAL)
<< "Empty entry at " << std::hex << entry.ea
<< " in segment " << cfg_seg->name;
}
}
return llvm::ConstantStruct::get(seg_type, entry_vals);
}
// Fill all the data segments in a given region.
static void FillSegment(const NativeModule *cfg_module,
const NativeSegment *cfg_seg) {
if (cfg_seg->entries.empty()) {
CHECK(cfg_seg->is_external && !cfg_seg->is_exported);
return;
}
auto seg = llvm::dyn_cast<llvm::GlobalVariable>(cfg_seg->Pointer());
CHECK_NOTNULL(seg);
if (seg->hasInitializer()) {
return; // Already initialized, e.g. due to `->Get()`.
}
auto seg_type = llvm::dyn_cast<llvm::StructType>(remill::GetValueType(seg));
// This might be null if there are two lifted variables with same name and
// one of them is exported and the exported variable is having xrefs or
// notnull.
CHECK_NOTNULL(seg_type);
seg->setInitializer(FillDataSegment(cfg_module, cfg_seg, seg_type));
}
} // namespace
llvm::Constant *NativeVariable::Pointer(void) const {
CHECK(!is_external);
CHECK_NOTNULL(segment);
const auto cfg_seg = segment->Get();
CHECK(!cfg_seg->is_external);
const auto &alias_name = is_exported && !name.empty() ? name : lifted_name;
auto alias = gModule->getNamedAlias(alias_name);
if (alias) {
return alias;
}
auto ret = LiftXrefInData(segment, ea, false /* cast_to_int */);
if (!FLAGS_disable_aliases && is_exported) {
auto ptr_type = llvm::dyn_cast<llvm::PointerType>(ret->getType());
auto alias = llvm::GlobalAlias::create(
ptr_type->getElementType(), ptr_type->getAddressSpace(),
llvm::GlobalValue::ExternalLinkage, alias_name, ret, gModule.get());
ret = alias;
module->AddNameToAddress(alias_name, ea);
if (cfg_seg->is_thread_local) {
alias->setThreadLocalMode(llvm::GlobalValue::InitialExecTLSModel);
}
}
return ret;
}
llvm::Constant *NativeVariable::Address(void) const {
return llvm::ConstantExpr::getPtrToInt(Pointer(), gWordType);
}
llvm::Constant *NativeSegment::Address(void) const {
auto ret = llvm::ConstantExpr::getPtrToInt(Pointer(), gWordType);
if (padding) {
ret = llvm::ConstantExpr::getAdd(
ret, llvm::ConstantInt::get(gWordType, padding));
}
return ret;
}
llvm::Constant *NativeSegment::Pointer(void) const {
const auto &var_name = (is_exported || is_external) ? name : lifted_name;
auto lifted_var = gModule->getGlobalVariable(
var_name, true /* AllowInternal */);
if (lifted_var) {
return lifted_var;
}
module->AddNameToAddress(var_name, ea);
if (is_external) {
LOG(INFO)
<< "Adding external segment " << name << " at "
<< std::hex << ea << std::dec;
llvm::Type* var_type = nullptr;
CHECK_NE(0, size)
<< "The size of the external variable '" << name << "' at "
<< std::hex << ea << std::dec << " cannot be zero";
// Handle external variables of up to 128 bits as intgers
// Anything else is treated as an array of bytes
switch(size) {
case 0:
// Why is this zero length? This should never happen
// Attempt a fix and output a warning
LOG(ERROR)
<< "The variable '" << name << "' at " << std::hex << ea
<< std::dec << " has size of zero; Assuming it should be size 1";
var_type = llvm::Type::getInt8Ty(*gContext);
break;
case 1: // 8 bit integer
case 2: // 16 bit integer
case 4: // 32 bit integer
case 8: // 64 bit integer
case 16: // 128 bit integer
var_type = llvm::Type::getIntNTy(
*gContext, static_cast<unsigned>(size * 8u));
break;
// An array of bytes
default: {
auto byte_type = llvm::Type::getInt8Ty(*gContext);
var_type = llvm::ArrayType::get(
byte_type, static_cast<unsigned>(size));
break;
}
}
lifted_var = new llvm::GlobalVariable(
*gModule, var_type, false, llvm::GlobalValue::ExternalLinkage,
nullptr, var_name, nullptr, ThreadLocalMode(this));
} else {
const auto linkage = is_exported ?
llvm::GlobalValue::ExternalLinkage :
llvm::GlobalValue::InternalLinkage;
LOG(INFO)
<< "Adding internal segment " << name;
lifted_var = new llvm::GlobalVariable(
*gModule, GetSegmentType(this), is_read_only,
linkage, nullptr, var_name, nullptr,
ThreadLocalMode(this));
}
if (ea) {
const auto alignment = 1u << __builtin_ctzl(ea - padding);
#if LLVM_VERSION_NUMBER >= LLVM_VERSION(10, 0)
lifted_var->setAlignment(llvm::MaybeAlign(alignment));
#else
lifted_var->setAlignment(alignment);
#endif
}
return lifted_var;
}
llvm::Function *GetOrCreateMcSemaInitializer(void) {
static llvm::Function *gInitFunc = nullptr;
if (gInitFunc) {
return gInitFunc;
}
LOG(INFO)
<< "Creating __mcsema_early_init function to pre-initialize runtime.";
gInitFunc = llvm::Function::Create(
llvm::FunctionType::get(llvm::Type::getVoidTy(*gContext), false),
llvm::GlobalValue::InternalLinkage,
"__mcsema_early_init", gModule.get());
auto bool_type = llvm::Type::getInt1Ty(*gContext);
auto check_var = new llvm::GlobalVariable(
*gModule, bool_type, false, llvm::GlobalValue::InternalLinkage,
llvm::Constant::getNullValue(bool_type));
auto entry = llvm::BasicBlock::Create(*gContext, "", gInitFunc);
auto already_done = llvm::BasicBlock::Create(*gContext, "", gInitFunc);
auto lazy_xref = llvm::BasicBlock::Create(*gContext, "", gInitFunc);
llvm::IRBuilder<> ir(entry);
auto guard = ir.CreateLoad(check_var, true);
ir.CreateCondBr(guard, already_done, lazy_xref);
ir.SetInsertPoint(already_done);
ir.CreateRetVoid();
// Last basic block will have lazy xrefs injected into it.
ir.SetInsertPoint(lazy_xref);
ir.CreateStore(llvm::ConstantInt::get(bool_type, 1), check_var, true);
ir.CreateRetVoid();
return gInitFunc;
}
void DefineDataSegments(const NativeModule *cfg_module) {
for (const auto &cfg_seg : cfg_module->segments) {
FillSegment(cfg_module, cfg_seg.get());
}
}
// Try to deduce what constructor and destructor functions should be used
bool DetectAndSetInitFiniCode(const NativeModule *cfg_module) {
// init_fini_pairs.size % 2 == 0 must hold
// constructor is first
std::vector<std::pair<std::string, bool>> init_fini_pairs = {
// Stripped binaries
{"init", false},
{"fini", false},
// Not stripped binaries
{"__libc_csu_init", false},
{"__libc_csu_fini", false},
};
for (const auto &cfg_func : cfg_module->ea_to_func) {
for (auto &init_fini_func : init_fini_pairs) {
if (cfg_func.second->name == init_fini_func.first) {
init_fini_func.second = true;
}
}
}
for (auto i = 0U; i < init_fini_pairs.size(); i += 2) {
if (init_fini_pairs[i].second && init_fini_pairs[i + 1].second) {
FLAGS_libc_constructor = init_fini_pairs[i].first;
FLAGS_libc_destructor = init_fini_pairs[i + 1].first;
LOG(INFO) << "Deduced libc ctor/dtor to "
<< FLAGS_libc_constructor << " / "
<< FLAGS_libc_destructor;
return true;
}
}
return false;
}
// Generate code to call pre-`main` function static object constructors, and
// post-`main` functions destructors.
void CallInitFiniCode(const NativeModule *cfg_module) {
if (FLAGS_libc_constructor.empty() && FLAGS_libc_destructor.empty()) {
if (!DetectAndSetInitFiniCode(cfg_module)) {
return;
}
}
for (const auto &entry : cfg_module->ea_to_func) {
const auto &cfg_func = entry.second;
llvm::Function *callback = nullptr;
llvm::Instruction *insert_point = nullptr;
if (FLAGS_libc_constructor.size() &&
FLAGS_libc_constructor == cfg_func->name) {
callback = llvm::dyn_cast<llvm::Function>(cfg_func->Pointer());
auto init_func = GetOrCreateMcSemaConstructor();
insert_point = &(init_func->front().back());
} else if (FLAGS_libc_destructor.size() &&
FLAGS_libc_destructor == cfg_func->name) {
callback = llvm::dyn_cast<llvm::Function>(cfg_func->Pointer());
auto fini_func = GetOrCreateMcSemaDestructor();
insert_point = &(fini_func->front().front());
}
if (insert_point && callback) {
llvm::IRBuilder<> ir(insert_point);
ir.CreateCall(callback);
}
}
}
} // namespace mcsema