Skip to content

Commit

Permalink
Inline asm external call
Browse files Browse the repository at this point in the history
  • Loading branch information
mishok2503 authored and MartinNowack committed Jul 4, 2022
1 parent 3d0033f commit 99c522b
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 60 deletions.
71 changes: 71 additions & 0 deletions include/klee/Module/KCallable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===-- KCallable.h ---------------------------------------------*- C++ -*-===//
//
// The KLEE Symbolic Virtual Machine
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef KLEE_KCALLABLE_H
#define KLEE_KCALLABLE_H

#include <string>

#include "llvm/ADT/Twine.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/LLVMContext.h"

namespace klee {
/// Wrapper for callable objects passed in callExternalFunction
class KCallable {
public:
enum CallableKind { CK_Function, CK_InlineAsm };

private:
const CallableKind Kind;

public:
KCallable(CallableKind Kind) : Kind(Kind) {}

CallableKind getKind() const { return Kind; }

virtual llvm::StringRef getName() const = 0;
virtual llvm::PointerType *getType() const = 0;
virtual llvm::Value *getValue() = 0;

virtual ~KCallable() = default;
};

class KInlineAsm : public KCallable {
private:
static unsigned getFreshAsmId() {
static unsigned globalId = 0;
return globalId++;
}

llvm::InlineAsm *value;
std::string name;

public:
KInlineAsm(llvm::InlineAsm *value)
: KCallable(CK_InlineAsm), value(value),
name("__asm__" + llvm::Twine(getFreshAsmId()).str()) {}

llvm::StringRef getName() const override { return name; }

llvm::PointerType *getType() const override { return value->getType(); }

llvm::Value *getValue() override { return value; }

static bool classof(const KCallable *callable) {
return callable->getKind() == CK_InlineAsm;
}

llvm::InlineAsm *getInlineAsm() { return value; }
};

} // namespace klee

#endif /* KLEE_KCALLABLE_H */
16 changes: 13 additions & 3 deletions include/klee/Module/KModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "klee/Config/Version.h"
#include "klee/Core/Interpreter.h"
#include "klee/Module/KCallable.h"

#include "llvm/ADT/ArrayRef.h"

Expand Down Expand Up @@ -39,7 +40,7 @@ namespace klee {
class KModule;
template<class T> class ref;

struct KFunction {
struct KFunction : public KCallable {
llvm::Function *function;

unsigned numArgs, numRegisters;
Expand All @@ -53,14 +54,23 @@ namespace klee {
/// "coverable" for statistics and search heuristics.
bool trackCoverage;

public:
explicit KFunction(llvm::Function*, KModule *);
explicit KFunction(llvm::Function*, KModule*);
KFunction(const KFunction &) = delete;
KFunction &operator=(const KFunction &) = delete;

~KFunction();

unsigned getArgRegister(unsigned index) { return index; }

llvm::StringRef getName() const override { return function->getName(); }

llvm::PointerType *getType() const override { return function->getType(); }

llvm::Value *getValue() override { return function; }

static bool classof(const KCallable *callable) {
return callable->getKind() == CK_Function;
}
};


Expand Down
47 changes: 29 additions & 18 deletions lib/Core/Executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "klee/Expr/ExprUtil.h"
#include "klee/Module/Cell.h"
#include "klee/Module/InstructionInfoTable.h"
#include "klee/Module/KCallable.h"
#include "klee/Module/KInstruction.h"
#include "klee/Module/KModule.h"
#include "klee/Solver/Common.h"
Expand All @@ -59,6 +60,7 @@
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
Expand Down Expand Up @@ -1658,10 +1660,11 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f,
return;
if (f && f->isDeclaration()) {
switch (f->getIntrinsicID()) {
case Intrinsic::not_intrinsic:
case Intrinsic::not_intrinsic: {
// state may be destroyed by this call, cannot touch
callExternalFunction(state, ki, f, arguments);
callExternalFunction(state, ki, kmodule->functionMap[f], arguments);
break;
}
case Intrinsic::fabs: {
ref<ConstantExpr> arg =
toConstant(state, arguments[0], "floating point");
Expand Down Expand Up @@ -2399,17 +2402,23 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) {
unsigned numArgs = cb.arg_size();
Function *f = getTargetFunction(fp, state);

if (isa<InlineAsm>(fp)) {
terminateStateOnExecError(state, "inline assembly is unsupported");
break;
}
// evaluate arguments
std::vector< ref<Expr> > arguments;
arguments.reserve(numArgs);

for (unsigned j=0; j<numArgs; ++j)
arguments.push_back(eval(ki, j+1, state).value);

if (auto* asmValue = dyn_cast<InlineAsm>(fp)) { //TODO: move to `executeCall`
if (ExternalCalls != ExternalCallPolicy::None) {
KInlineAsm callable(asmValue);
callExternalFunction(state, ki, &callable, arguments);
} else {
terminateStateOnExecError(state, "external calls disallowed (in particular inline asm)");
}
break;
}

if (f) {
const FunctionType *fType =
dyn_cast<FunctionType>(cast<PointerType>(f->getType())->getElementType());
Expand Down Expand Up @@ -3784,16 +3793,18 @@ static std::set<std::string> okExternals(okExternalsList,

void Executor::callExternalFunction(ExecutionState &state,
KInstruction *target,
Function *function,
KCallable *callable,
std::vector< ref<Expr> > &arguments) {
// check if specialFunctionHandler wants it
if (specialFunctionHandler->handle(state, function, target, arguments))
return;
if (const auto *func = dyn_cast<KFunction>(callable)) {
if (specialFunctionHandler->handle(state, func->function, target, arguments))
return;
}

if (ExternalCalls == ExternalCallPolicy::None &&
!okExternals.count(function->getName().str())) {
!okExternals.count(callable->getName().str())) {
klee_warning("Disallowed call to external function: %s\n",
function->getName().str().c_str());
callable->getName().str().c_str());
terminateStateOnUserError(state, "external calls disallowed");
return;
}
Expand Down Expand Up @@ -3835,7 +3846,7 @@ void Executor::callExternalFunction(ExecutionState &state,
} else {
terminateStateOnExecError(state,
"external call with symbolic argument: " +
function->getName());
callable->getName());
return;
}
}
Expand All @@ -3856,7 +3867,7 @@ void Executor::callExternalFunction(ExecutionState &state,
if (!errnoValue) {
terminateStateOnExecError(state,
"external call with errno value symbolic: " +
function->getName());
callable->getName());
return;
}

Expand All @@ -3868,7 +3879,7 @@ void Executor::callExternalFunction(ExecutionState &state,

std::string TmpStr;
llvm::raw_string_ostream os(TmpStr);
os << "calling external: " << function->getName().str() << "(";
os << "calling external: " << callable->getName().str() << "(";
for (unsigned i=0; i<arguments.size(); i++) {
os << arguments[i];
if (i != arguments.size()-1)
Expand All @@ -3879,12 +3890,12 @@ void Executor::callExternalFunction(ExecutionState &state,
if (AllExternalWarnings)
klee_warning("%s", os.str().c_str());
else
klee_warning_once(function, "%s", os.str().c_str());
klee_warning_once(callable->getValue(), "%s", os.str().c_str());
}

bool success = externalDispatcher->executeCall(function, target->inst, args);
bool success = externalDispatcher->executeCall(callable, target->inst, args);
if (!success) {
terminateStateOnError(state, "failed external call: " + function->getName(),
terminateStateOnError(state, "failed external call: " + callable->getName(),
StateTerminationType::External);
return;
}
Expand All @@ -3903,7 +3914,7 @@ void Executor::callExternalFunction(ExecutionState &state,
#endif

Type *resultType = target->inst->getType();
if (resultType != Type::getVoidTy(function->getContext())) {
if (resultType != Type::getVoidTy(kmodule->module->getContext())) {
ref<Expr> e = ConstantExpr::fromMemory((void*) args,
getWidthForLLVMType(resultType));
bindLocal(target, state, e);
Expand Down
3 changes: 2 additions & 1 deletion lib/Core/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ namespace klee {
class ExternalDispatcher;
class Expr;
class InstructionInfoTable;
class KCallable;
struct KFunction;
struct KInstruction;
class KInstIterator;
Expand Down Expand Up @@ -240,7 +241,7 @@ class Executor : public Interpreter {

void callExternalFunction(ExecutionState &state,
KInstruction *target,
llvm::Function *function,
KCallable *callable,
std::vector< ref<Expr> > &arguments);

ObjectState *bindObjectInState(ExecutionState &state, const MemoryObject *mo,
Expand Down
35 changes: 23 additions & 12 deletions lib/Core/ExternalDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@

#include "ExternalDispatcher.h"
#include "klee/Config/Version.h"
#include "klee/Module/KCallable.h"
#include "klee/Module/KModule.h"

#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/InlineAsm.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
Expand Down Expand Up @@ -45,7 +48,7 @@ class ExternalDispatcherImpl {
private:
typedef std::map<const llvm::Instruction *, llvm::Function *> dispatchers_ty;
dispatchers_ty dispatchers;
llvm::Function *createDispatcher(llvm::Function *f, llvm::Instruction *i,
llvm::Function *createDispatcher(KCallable *target, llvm::Instruction *i,
llvm::Module *module);
llvm::ExecutionEngine *executionEngine;
LLVMContext &ctx;
Expand All @@ -59,7 +62,7 @@ class ExternalDispatcherImpl {
public:
ExternalDispatcherImpl(llvm::LLVMContext &ctx);
~ExternalDispatcherImpl();
bool executeCall(llvm::Function *function, llvm::Instruction *i,
bool executeCall(KCallable *callable, llvm::Instruction *i,
uint64_t *args);
void *resolveSymbol(const std::string &name);
int getLastErrno();
Expand Down Expand Up @@ -153,7 +156,7 @@ ExternalDispatcherImpl::~ExternalDispatcherImpl() {
// we don't need to delete any of them.
}

bool ExternalDispatcherImpl::executeCall(Function *f, Instruction *i,
bool ExternalDispatcherImpl::executeCall(KCallable *callable, Instruction *i,
uint64_t *args) {
dispatchers_ty::iterator it = dispatchers.find(i);
if (it != dispatchers.end()) {
Expand All @@ -180,7 +183,7 @@ bool ExternalDispatcherImpl::executeCall(Function *f, Instruction *i,
// The MCJIT generates whole modules at a time so for every call that we
// haven't made before we need to create a new Module.
dispatchModule = new Module(getFreshModuleID(), ctx);
dispatcher = createDispatcher(f, i, dispatchModule);
dispatcher = createDispatcher(callable, i, dispatchModule);
dispatchers.insert(std::make_pair(i, dispatcher));

// Force the JIT execution engine to go ahead and build the function. This
Expand Down Expand Up @@ -249,10 +252,10 @@ bool ExternalDispatcherImpl::runProtectedCall(Function *f, uint64_t *args) {
// the special cases that the JIT knows how to directly call. If this is not
// done, then the jit will end up generating a nullary stub just to call our
// stub, for every single function call.
Function *ExternalDispatcherImpl::createDispatcher(Function *target,
Function *ExternalDispatcherImpl::createDispatcher(KCallable *target,
Instruction *inst,
Module *module) {
if (!resolveSymbol(target->getName().str()))
if (isa<KFunction>(target) && !resolveSymbol(target->getName().str()))
return 0;

const CallBase &cb = cast<CallBase>(*inst);
Expand Down Expand Up @@ -309,10 +312,18 @@ Function *ExternalDispatcherImpl::createDispatcher(Function *target,
idx += ((!!argSize ? argSize : 64) + 63) / 64;
}

auto dispatchTarget = module->getOrInsertFunction(target->getName(), FTy,
target->getAttributes());
auto result = Builder.CreateCall(dispatchTarget,
llvm::ArrayRef<Value *>(args, args + i));
llvm::CallInst *result;
if (auto* func = dyn_cast<KFunction>(target)) {
auto dispatchTarget = module->getOrInsertFunction(target->getName(), FTy,
func->function->getAttributes());
result = Builder.CreateCall(dispatchTarget,
llvm::ArrayRef<Value *>(args, args + i));
} else if (auto* asmValue = dyn_cast<KInlineAsm>(target)) {
result = Builder.CreateCall(asmValue->getInlineAsm(),
llvm::ArrayRef<Value *>(args, args + i));
} else {
assert(0 && "Unhandled KCallable derived class");
}
if (result->getType() != Type::getVoidTy(ctx)) {
auto resp = Builder.CreateBitCast(
argI64s, PointerType::getUnqual(result->getType()));
Expand All @@ -336,9 +347,9 @@ ExternalDispatcher::ExternalDispatcher(llvm::LLVMContext &ctx)

ExternalDispatcher::~ExternalDispatcher() { delete impl; }

bool ExternalDispatcher::executeCall(llvm::Function *function,
bool ExternalDispatcher::executeCall(KCallable *callable,
llvm::Instruction *i, uint64_t *args) {
return impl->executeCall(function, i, args);
return impl->executeCall(callable, i, args);
}

void *ExternalDispatcher::resolveSymbol(const std::string &name) {
Expand Down
4 changes: 2 additions & 2 deletions lib/Core/ExternalDispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
namespace llvm {
class Instruction;
class LLVMContext;
class Function;
}

namespace klee {
class ExternalDispatcherImpl;
class KCallable;
class ExternalDispatcher {
private:
ExternalDispatcherImpl *impl;
Expand All @@ -37,7 +37,7 @@ class ExternalDispatcher {
* ci with arguments in args[1], args[2], ... and writing the result
* into args[0].
*/
bool executeCall(llvm::Function *function, llvm::Instruction *i,
bool executeCall(KCallable *callable, llvm::Instruction *i,
uint64_t *args);
void *resolveSymbol(const std::string &name);

Expand Down

0 comments on commit 99c522b

Please sign in to comment.