f18dc607 |
/* |
e32caecb |
* JIT compile ClamAV bytecode. |
f18dc607 |
*
* Copyright (C) 2009 Sourcefire, Inc.
*
* Authors: Török Edvin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/ |
6b67ec6e |
#define DEBUG_TYPE "clamavjit" |
daffb518 |
#include "llvm/ADT/DenseMap.h" |
d0af4afe |
#include "llvm/CallingConv.h" |
3b33bd68 |
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h" |
ee8f1888 |
#include "llvm/PassManager.h" |
3b33bd68 |
#include "llvm/ModuleProvider.h"
#include "llvm/Support/Compiler.h" |
6b67ec6e |
#include "llvm/Support/Debug.h" |
3b33bd68 |
#include "llvm/Support/CommandLine.h" |
d1487222 |
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h" |
3b33bd68 |
#include "llvm/Support/IRBuilder.h"
#include "llvm/Support/PrettyStackTrace.h" |
d1487222 |
#include "llvm/System/Signals.h" |
3b33bd68 |
#include "llvm/System/Threading.h" |
d1487222 |
#include "llvm/Target/TargetSelect.h" |
ee8f1888 |
#include "llvm/Target/TargetData.h" |
3b33bd68 |
#include "llvm/Support/TargetFolder.h" |
ee8f1888 |
#include "llvm/Analysis/Verifier.h" |
6b67ec6e |
#include "llvm/Transforms/Scalar.h"
#include "llvm/System/ThreadLocal.h" |
d1487222 |
#include <cstdlib> |
6b67ec6e |
#include <csetjmp> |
d1487222 |
#include <new>
|
2c7d5adc |
#include "llvm/Config/config.h"
#ifndef LLVM_MULTITHREADED
#error "Multithreading support must be available to LLVM!"
#endif |
d1487222 |
#include "clamav.h"
#include "clambc.h"
#include "bytecode_priv.h"
#include "bytecode.h"
#define MODULE "libclamav JIT: "
using namespace llvm; |
daffb518 |
typedef DenseMap<const struct cli_bc_func*, void*> FunctionMapTy; |
d1487222 |
struct cli_bcengine {
ExecutionEngine *EE;
LLVMContext Context; |
3b33bd68 |
FunctionMapTy compiledFunctions; |
d1487222 |
};
namespace {
|
6b67ec6e |
static sys::ThreadLocal<const jmp_buf> ExceptionReturn;
|
3b33bd68 |
void do_shutdown() {
llvm_shutdown();
} |
daffb518 |
|
6b67ec6e |
static void NORETURN jit_exception_handler(void)
{
longjmp(*const_cast<jmp_buf*>(ExceptionReturn.get()), 1);
}
|
3b33bd68 |
void llvm_error_handler(void *user_data, const std::string &reason)
{
errs() << reason; |
6b67ec6e |
jit_exception_handler(); |
3b33bd68 |
} |
daffb518 |
|
e32caecb |
class LLVMTypeMapper { |
3b33bd68 |
private: |
e32caecb |
std::vector<PATypeHolder> TypeMap; |
3b33bd68 |
LLVMContext &Context; |
e32caecb |
unsigned numTypes;
const Type *getStatic(uint16_t ty) |
3b33bd68 |
{
if (!ty)
return Type::getVoidTy(Context); |
ee8f1888 |
if (ty <= 64) |
3b33bd68 |
return IntegerType::get(Context, ty);
switch (ty) {
case 65:
return PointerType::getUnqual(Type::getInt8Ty(Context));
case 66:
return PointerType::getUnqual(Type::getInt16Ty(Context));
case 67:
return PointerType::getUnqual(Type::getInt32Ty(Context));
case 68:
return PointerType::getUnqual(Type::getInt64Ty(Context));
} |
e32caecb |
llvm_unreachable("getStatic");
}
public:
LLVMTypeMapper(LLVMContext &Context, const struct cli_bc_type *types, |
d0af4afe |
unsigned count, const Type *Hidden=0) : Context(Context), numTypes(count) |
e32caecb |
{
TypeMap.reserve(count);
// During recursive type construction pointers to Type* may be
// invalidated, so we must use a TypeHolder to an Opaque type as a
// start.
for (unsigned i=0;i<count;i++) {
TypeMap.push_back(OpaqueType::get(Context));
}
std::vector<const Type*> Elts;
for (unsigned i=0;i<count;i++) {
const struct cli_bc_type *type = &types[i];
Elts.clear();
unsigned n = type->kind == DArrayType ? 1 : type->numElements;
for (unsigned j=0;j<n;j++) {
Elts.push_back(get(type->containedTypes[j]));
}
const Type *Ty;
switch (type->kind) {
case DFunctionType:
{
assert(Elts.size() > 0 && "Function with no return type?");
const Type *RetTy = Elts[0]; |
d0af4afe |
if (Hidden)
Elts[0] = Hidden;
else
Elts.erase(Elts.begin()); |
e32caecb |
Ty = FunctionType::get(RetTy, Elts, false);
break;
}
case DPointerType:
Ty = PointerType::getUnqual(Elts[0]);
break;
case DStructType:
Ty = StructType::get(Context, Elts);
break;
case DPackedStructType:
Ty = StructType::get(Context, Elts, true);
break;
case DArrayType:
Ty = ArrayType::get(Elts[0], type->numElements);
break;
}
// Make the opaque type a concrete type, doing recursive type
// unification if needed.
cast<OpaqueType>(TypeMap[i].get())->refineAbstractTypeTo(Ty);
}
}
const Type *get(uint16_t ty)
{
if (ty < 69)
return getStatic(ty); |
3b33bd68 |
ty -= 69; |
e32caecb |
assert(ty < numTypes && "TypeID out of range");
return TypeMap[ty].get(); |
3b33bd68 |
} |
e32caecb |
}; |
3b33bd68 |
|
daffb518 |
|
e32caecb |
class VISIBILITY_HIDDEN LLVMCodegen {
private:
const struct cli_bc *bc;
Module *M;
LLVMContext &Context;
LLVMTypeMapper *TypeMap;
Function **apiFuncs;
FunctionMapTy &compiledFunctions;
Twine BytecodeID;
ExecutionEngine *EE;
TargetFolder Folder;
IRBuilder<false, TargetFolder> Builder;
Value **Values;
FunctionPassManager &PM;
unsigned numLocals;
unsigned numArgs; |
3b33bd68 |
|
e6d1fe78 |
Value *getOperand(const struct cli_bc_func *func, const Type *Ty, operand_t operand)
{
unsigned map[] = {0, 1, 2, 3, 3, 4, 4, 4, 4};
if (operand < func->numValues)
return Values[operand];
unsigned w = (Ty->getPrimitiveSizeInBits()+7)/8;
return convertOperand(func, map[w], operand);
}
|
ee8f1888 |
Value *convertOperand(const struct cli_bc_func *func, const Type *Ty, operand_t operand)
{
unsigned map[] = {0, 1, 2, 3, 3, 4, 4, 4, 4};
if (operand < func->numArgs)
return Values[operand]; |
e6d1fe78 |
if (operand < func->numValues) {
Value *V = Values[operand];
if (V->getType() == Ty)
return V;
return Builder.CreateLoad(V);
} |
ee8f1888 |
unsigned w = (Ty->getPrimitiveSizeInBits()+7)/8;
return convertOperand(func, map[w], operand);
}
Value *convertOperand(const struct cli_bc_func *func, |
3b33bd68 |
const struct cli_bc_inst *inst, operand_t operand)
{ |
ee8f1888 |
return convertOperand(func, inst->interp_op%5, operand);
}
Value *convertOperand(const struct cli_bc_func *func,
unsigned w, operand_t operand) {
if (operand < func->numArgs)
return Values[operand];
if (operand < func->numValues)
return Builder.CreateLoad(Values[operand]);
// Constant
operand -= func->numValues;
// This was already validated by libclamav. |
6b67ec6e |
assert(operand < func->numConstants && "Constant out of range"); |
ee8f1888 |
uint64_t *c = &func->constants[operand];
uint64_t v;
const Type *Ty;
switch (w) {
case 0:
case 1: |
e32caecb |
Ty = w ? Type::getInt8Ty(Context) : |
ee8f1888 |
Type::getInt1Ty(Context);
v = *(uint8_t*)c;
break;
case 2:
Ty = Type::getInt16Ty(Context);
v = *(uint16_t*)c;
break;
case 3:
Ty = Type::getInt32Ty(Context);
v = *(uint32_t*)c;
break;
case 4:
Ty = Type::getInt64Ty(Context);
v = *(uint64_t*)c;
break; |
3b33bd68 |
} |
ee8f1888 |
return ConstantInt::get(Ty, v);
}
void Store(uint16_t dest, Value *V)
{
assert(dest >= numArgs && dest < numLocals+numArgs && "Instruction destination out of range");
Builder.CreateStore(V, Values[dest]); |
3b33bd68 |
} |
6b67ec6e |
// Insert code that calls \arg FHandler if \arg FailCond is true.
void InsertVerify(Value *FailCond, BasicBlock *&Fail, Function *FHandler,
Function *F) {
if (!Fail) {
Fail = BasicBlock::Create(Context, "fail", F);
CallInst::Create(FHandler,"",Fail);
new UnreachableInst(Context, Fail);
}
BasicBlock *OkBB = BasicBlock::Create(Context, "", F);
Builder.CreateCondBr(FailCond, Fail, OkBB);
Builder.SetInsertPoint(OkBB);
} |
e32caecb |
const Type* mapType(uint16_t typeID)
{
return TypeMap->get(typeID);
} |
3b33bd68 |
public:
LLVMCodegen(const struct cli_bc *bc, Module *M, FunctionMapTy &cFuncs, |
e32caecb |
ExecutionEngine *EE, FunctionPassManager &PM, Function **apiFuncs) |
d0af4afe |
: bc(bc), M(M), Context(M->getContext()), compiledFunctions(cFuncs),
BytecodeID("bc"+Twine(bc->id)), EE(EE),
Folder(EE->getTargetData(), Context), Builder(Context, Folder), PM(PM),
apiFuncs(apiFuncs) |
e32caecb |
{} |
daffb518 |
|
ee8f1888 |
bool generate() { |
3b33bd68 |
PrettyStackTraceString Trace(BytecodeID.str().c_str()); |
e32caecb |
TypeMap = new LLVMTypeMapper(Context, bc->types + 4, bc->num_types - 5); |
6b67ec6e |
|
c466339d |
FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context), |
6b67ec6e |
false);
Function *FHandler = Function::Create(FTy, Function::InternalLinkage,
"clamjit.fail", M);
FHandler->setDoesNotReturn();
FHandler->setDoesNotThrow();
FHandler->addFnAttr(Attribute::NoInline);
EE->addGlobalMapping(FHandler, (void*)jit_exception_handler);
|
d0af4afe |
// The hidden ctx param to all functions
const Type *HiddenCtx = PointerType::getUnqual(Type::getInt8Ty(Context));
|
ee8f1888 |
Function **Functions = new Function*[bc->num_func]; |
daffb518 |
for (unsigned j=0;j<bc->num_func;j++) { |
ee8f1888 |
PrettyStackTraceString CrashInfo("Generate LLVM IR functions"); |
3b33bd68 |
// Create LLVM IR Function |
daffb518 |
const struct cli_bc_func *func = &bc->funcs[j];
std::vector<const Type*> argTypes; |
d0af4afe |
argTypes.push_back(HiddenCtx); |
daffb518 |
for (unsigned a=0;a<func->numArgs;a++) { |
3b33bd68 |
argTypes.push_back(mapType(func->types[a]));
}
const Type *RetTy = mapType(func->returnType); |
c466339d |
FunctionType *FTy = FunctionType::get(RetTy, argTypes, |
3b33bd68 |
false); |
d0af4afe |
Functions[j] = Function::Create(FTy, Function::InternalLinkage, |
3b33bd68 |
BytecodeID+"f"+Twine(j), M); |
6b67ec6e |
Functions[j]->setDoesNotThrow(); |
d0af4afe |
Functions[j]->setCallingConv(CallingConv::Fast); |
ee8f1888 |
} |
e6d1fe78 |
const Type *I32Ty = Type::getInt32Ty(Context); |
ee8f1888 |
for (unsigned j=0;j<bc->num_func;j++) {
PrettyStackTraceString CrashInfo("Generate LLVM IR");
const struct cli_bc_func *func = &bc->funcs[j]; |
e32caecb |
|
3b33bd68 |
// Create all BasicBlocks |
ee8f1888 |
Function *F = Functions[j]; |
3b33bd68 |
BasicBlock **BB = new BasicBlock*[func->numBB];
for (unsigned i=0;i<func->numBB;i++) {
BB[i] = BasicBlock::Create(Context, "", F); |
daffb518 |
} |
3b33bd68 |
|
6b67ec6e |
BasicBlock *Fail = 0; |
ee8f1888 |
Values = new Value*[func->numValues];
Builder.SetInsertPoint(BB[0]);
Function::arg_iterator I = F->arg_begin(); |
d0af4afe |
assert(F->arg_size() == func->numArgs + 1 && "Mismatched args");
++I; |
ee8f1888 |
for (unsigned i=0;i<func->numArgs; i++) {
assert(I != F->arg_end());
Values[i] = &*I;
++I;
}
for (unsigned i=func->numArgs;i<func->numValues;i++) { |
479fa713 |
if (!func->types[i]) {
//instructions without return value, like store
Values[i] = 0;
continue;
} |
ee8f1888 |
Values[i] = Builder.CreateAlloca(mapType(func->types[i]));
}
numLocals = func->numLocals;
numArgs = func->numArgs; |
3b33bd68 |
// Generate LLVM IR for each BB
for (unsigned i=0;i<func->numBB;i++) {
const struct cli_bc_bb *bb = &func->BB[i];
Builder.SetInsertPoint(BB[i]);
for (unsigned j=0;j<bb->numInsts;j++) { |
ee8f1888 |
const struct cli_bc_inst *inst = &bb->insts[j];
Value *Op0, *Op1, *Op2;
// libclamav has already validated this.
assert(inst->opcode < OP_INVALID && "Invalid opcode");
switch (inst->opcode) {
case OP_JMP:
case OP_BRANCH:
case OP_CALL_API:
case OP_CALL_DIRECT:
case OP_ZEXT:
case OP_SEXT:
case OP_TRUNC: |
e6d1fe78 |
case OP_GEP1:
case OP_GEP2:
case OP_GEPN:
case OP_STORE: |
a1781898 |
case OP_COPY: |
ee8f1888 |
// these instructions represents operands differently
break;
default:
switch (operand_counts[inst->opcode]) {
case 1:
Op0 = convertOperand(func, inst, inst->u.unaryop);
break;
case 2:
Op0 = convertOperand(func, inst, inst->u.binop[0]);
Op1 = convertOperand(func, inst, inst->u.binop[1]);
break;
case 3:
Op0 = convertOperand(func, inst, inst->u.three[0]);
Op1 = convertOperand(func, inst, inst->u.three[1]);
Op2 = convertOperand(func, inst, inst->u.three[2]);
break;
}
} |
3b33bd68 |
switch (inst->opcode) { |
ee8f1888 |
case OP_ADD:
Store(inst->dest, Builder.CreateAdd(Op0, Op1));
break;
case OP_SUB:
Store(inst->dest, Builder.CreateSub(Op0, Op1));
break;
case OP_MUL:
Store(inst->dest, Builder.CreateMul(Op0, Op1));
break;
case OP_UDIV: |
6b67ec6e |
{
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
InsertVerify(Bad, Fail, FHandler, F); |
ee8f1888 |
Store(inst->dest, Builder.CreateUDiv(Op0, Op1));
break; |
6b67ec6e |
} |
ee8f1888 |
case OP_SDIV: |
6b67ec6e |
{
//TODO: also verify Op0 == -1 && Op1 = INT_MIN
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
InsertVerify(Bad, Fail, FHandler, F); |
ee8f1888 |
Store(inst->dest, Builder.CreateSDiv(Op0, Op1));
break; |
6b67ec6e |
} |
ee8f1888 |
case OP_UREM: |
6b67ec6e |
{
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
InsertVerify(Bad, Fail, FHandler, F); |
ee8f1888 |
Store(inst->dest, Builder.CreateURem(Op0, Op1));
break; |
6b67ec6e |
} |
ee8f1888 |
case OP_SREM: |
6b67ec6e |
{
//TODO: also verify Op0 == -1 && Op1 = INT_MIN
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0));
InsertVerify(Bad, Fail, FHandler, F); |
ee8f1888 |
Store(inst->dest, Builder.CreateSRem(Op0, Op1));
break; |
6b67ec6e |
} |
ee8f1888 |
case OP_SHL:
Store(inst->dest, Builder.CreateShl(Op0, Op1));
break;
case OP_LSHR:
Store(inst->dest, Builder.CreateLShr(Op0, Op1));
break;
case OP_ASHR:
Store(inst->dest, Builder.CreateAShr(Op0, Op1));
break;
case OP_AND:
Store(inst->dest, Builder.CreateAnd(Op0, Op1));
break;
case OP_OR:
Store(inst->dest, Builder.CreateOr(Op0, Op1));
break;
case OP_XOR:
Store(inst->dest, Builder.CreateXor(Op0, Op1));
break;
case OP_TRUNC:
{
Value *Src = convertOperand(func, inst, inst->u.cast.source);
const Type *Ty = mapType(func->types[inst->dest]);
Store(inst->dest, Builder.CreateTrunc(Src, Ty));
break;
}
case OP_ZEXT:
{
Value *Src = convertOperand(func, inst, inst->u.cast.source);
const Type *Ty = mapType(func->types[inst->dest]);
Store(inst->dest, Builder.CreateZExt(Src, Ty));
break;
}
case OP_SEXT:
{
Value *Src = convertOperand(func, inst, inst->u.cast.source);
const Type *Ty = mapType(func->types[inst->dest]);
Store(inst->dest, Builder.CreateSExt(Src, Ty));
break;
}
case OP_BRANCH:
{
Value *Cond = convertOperand(func, inst, inst->u.branch.condition);
BasicBlock *True = BB[inst->u.branch.br_true];
BasicBlock *False = BB[inst->u.branch.br_false];
if (Cond->getType() != Type::getInt1Ty(Context)) {
errs() << MODULE << "type mismatch in condition\n";
return false;
}
Builder.CreateCondBr(Cond, True, False);
break;
}
case OP_JMP:
{
BasicBlock *Jmp = BB[inst->u.jump];
Builder.CreateBr(Jmp);
break;
} |
3b33bd68 |
case OP_RET: |
ee8f1888 |
Builder.CreateRet(Op0);
break;
case OP_ICMP_EQ:
Store(inst->dest, Builder.CreateICmpEQ(Op0, Op1));
break;
case OP_ICMP_NE:
Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
break;
case OP_ICMP_UGT: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpUGT(Op0, Op1)); |
ee8f1888 |
break;
case OP_ICMP_UGE: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpUGE(Op0, Op1)); |
3b33bd68 |
break; |
ee8f1888 |
case OP_ICMP_ULT: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpULT(Op0, Op1)); |
ee8f1888 |
break;
case OP_ICMP_ULE: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpULE(Op0, Op1)); |
ee8f1888 |
break;
case OP_ICMP_SGT: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpSGT(Op0, Op1)); |
ee8f1888 |
break;
case OP_ICMP_SGE: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpSGE(Op0, Op1)); |
ee8f1888 |
break;
case OP_ICMP_SLT: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpSLT(Op0, Op1)); |
ee8f1888 |
break;
case OP_SELECT:
Store(inst->dest, Builder.CreateSelect(Op0, Op1, Op2));
break;
case OP_COPY: |
a1781898 |
{
Value *Dest = Values[inst->u.binop[1]];
const PointerType *PTy = cast<PointerType>(Dest->getType());
Op0 = convertOperand(func, PTy->getElementType(), inst->u.binop[0]);
Builder.CreateStore(Op0, Dest); |
ee8f1888 |
break; |
a1781898 |
} |
ee8f1888 |
case OP_CALL_DIRECT:
{
Function *DestF = Functions[inst->u.ops.funcid];
SmallVector<Value*, 2> args; |
d0af4afe |
args.push_back(&*F->arg_begin()); // pass hidden arg |
ee8f1888 |
for (unsigned a=0;a<inst->u.ops.numOps;a++) {
operand_t op = inst->u.ops.ops[a]; |
d0af4afe |
args.push_back(convertOperand(func, DestF->getFunctionType()->getParamType(a+1), op)); |
ee8f1888 |
} |
d0af4afe |
CallInst *CI = Builder.CreateCall(DestF, args.begin(), args.end());
CI->setCallingConv(CallingConv::Fast);
Store(inst->dest, CI); |
ee8f1888 |
break;
} |
e32caecb |
case OP_CALL_API:
{
assert(inst->u.ops.funcid < cli_apicall_maxapi && "APICall out of range");
const struct cli_apicall *api = &cli_apicalls[inst->u.ops.funcid];
std::vector<Value*> args;
Function *DestF = apiFuncs[inst->u.ops.funcid]; |
d0af4afe |
args.push_back(&*F->arg_begin()); // pass hidden arg |
e32caecb |
for (unsigned a=0;a<inst->u.ops.numOps;a++) {
operand_t op = inst->u.ops.ops[a]; |
d0af4afe |
args.push_back(convertOperand(func, DestF->getFunctionType()->getParamType(a+1), op)); |
e32caecb |
}
Store(inst->dest, Builder.CreateCall(DestF, args.begin(), args.end()));
break;
} |
e6d1fe78 |
case OP_GEP1:
{
Value *V = Values[inst->u.binop[0]];
Value *Op = convertOperand(func, I32Ty, inst->u.binop[1]);
Store(inst->dest, Builder.CreateGEP(V, Op));
break;
}
case OP_GEP2:
{
std::vector<Value*> Idxs;
Value *V = Values[inst->u.three[0]];
Idxs.push_back(convertOperand(func, I32Ty, inst->u.three[1]));
Idxs.push_back(convertOperand(func, I32Ty, inst->u.three[2]));
Store(inst->dest, Builder.CreateGEP(V, Idxs.begin(), Idxs.end()));
break;
}
case OP_GEPN:
{
std::vector<Value*> Idxs;
assert(inst->u.ops.numOps > 1);
Value *V = Values[inst->u.ops.ops[0]];
for (unsigned a=1;a<inst->u.ops.numOps;a++)
Idxs.push_back(convertOperand(func, I32Ty, inst->u.ops.ops[a]));
Store(inst->dest, Builder.CreateGEP(V, Idxs.begin(), Idxs.end()));
break;
}
case OP_STORE:
{
Value *Dest = convertOperand(func, inst, inst->u.binop[1]);
const Type *ETy = cast<PointerType>(Dest->getType())->getElementType();
Builder.CreateStore(getOperand(func, ETy, inst->u.binop[0]),
Dest);
break;
}
case OP_LOAD: |
a1781898 |
Op0 = Builder.CreateLoad(Op0); |
e6d1fe78 |
Store(inst->dest, Op0);
break; |
ee8f1888 |
default: |
2487a4a3 |
errs() << "JIT doesn't implement opcode " <<
inst->opcode << " yet!\n";
return false; |
3b33bd68 |
}
}
}
|
ee8f1888 |
if (verifyFunction(*F, PrintMessageAction)) {
errs() << MODULE << "Verification failed\n";
// verification failed
return false;
}
PM.run(*F);
delete [] Values; |
6b67ec6e |
delete [] BB; |
ee8f1888 |
}
|
6b67ec6e |
DEBUG(M->dump()); |
e32caecb |
delete TypeMap; |
d0af4afe |
std::vector<const Type*> args;
args.push_back(PointerType::getUnqual(Type::getInt8Ty(Context)));
FunctionType *Callable = FunctionType::get(Type::getInt32Ty(Context),
args, false); |
ee8f1888 |
for (unsigned j=0;j<bc->num_func;j++) {
const struct cli_bc_func *func = &bc->funcs[j]; |
3b33bd68 |
PrettyStackTraceString CrashInfo2("Native machine codegen"); |
6b67ec6e |
// If prototype matches, add to callable functions |
d0af4afe |
if (Functions[j]->getFunctionType() == Callable) {
// All functions have the Fast calling convention, however
// entrypoint can only be C, emit wrapper
Function *F = Function::Create(Functions[j]->getFunctionType(),
Function::ExternalLinkage,
Functions[j]->getName()+"_wrap", M);
F->setDoesNotThrow();
BasicBlock *BB = BasicBlock::Create(Context, "", F);
std::vector<Value*> args;
for (Function::arg_iterator J=F->arg_begin(),
JE=F->arg_end(); J != JE; ++JE) {
args.push_back(&*J);
}
CallInst *CI = CallInst::Create(Functions[j], args.begin(), args.end(), "", BB);
CI->setCallingConv(CallingConv::Fast);
ReturnInst::Create(Context, CI, BB);
if (verifyFunction(*F, PrintMessageAction));
// Codegen current function as executable machine code.
void *code = EE->getPointerToFunction(F);
|
6b67ec6e |
compiledFunctions[func] = code; |
d0af4afe |
} |
daffb518 |
} |
6b67ec6e |
delete [] Functions; |
ee8f1888 |
return true; |
daffb518 |
} |
3b33bd68 |
}; |
d1487222 |
}
|
85a25497 |
int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx,
const struct cli_bc_func *func) |
d1487222 |
{ |
6b67ec6e |
jmp_buf env; |
85a25497 |
void *code = bcs->engine->compiledFunctions[func]; |
6b67ec6e |
if (!code) {
errs() << MODULE << "Unable to find compiled function\n";
return CL_EBYTECODE;
} |
85a25497 |
// execute; |
6b67ec6e |
if (setjmp(env) == 0) {
// setup exception handler to longjmp back here
ExceptionReturn.set(&env); |
d0af4afe |
uint32_t result = ((uint32_t (*)(struct cli_bc_ctx *))code)(ctx); |
6b67ec6e |
*(uint32_t*)ctx->values = result;
return 0;
}
errs() << "\n";
errs().changeColor(raw_ostream::RED, true) << MODULE
<< "*** JITed code intercepted runtime error!\n";
errs().resetColor();
return CL_EBYTECODE; |
d1487222 |
}
|
daffb518 |
|
d1487222 |
int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
{ |
2487a4a3 |
if (!bcs->engine)
return CL_EBYTECODE; |
6b67ec6e |
jmp_buf env;
// setup exception handler to longjmp back here
ExceptionReturn.set(&env);
if (setjmp(env) != 0) {
errs() << "\n";
errs().changeColor(raw_ostream::RED, true) << MODULE
<< "*** FATAL error encountered during bytecode generation\n";
errs().resetColor();
return CL_EBYTECODE;
} |
d1487222 |
// LLVM itself never throws exceptions, but operator new may throw bad_alloc
try {
Module *M = new Module("ClamAV jit module", bcs->engine->Context); |
ee8f1888 |
ExistingModuleProvider *MP = new ExistingModuleProvider(M); |
d1487222 |
{
// Create the JIT.
std::string ErrorMsg; |
ee8f1888 |
EngineBuilder builder(MP); |
d1487222 |
builder.setErrorStr(&ErrorMsg);
builder.setEngineKind(EngineKind::JIT);
builder.setOptLevel(CodeGenOpt::Aggressive);
ExecutionEngine *EE = bcs->engine->EE = builder.create();
if (!EE) {
if (!ErrorMsg.empty())
errs() << MODULE << "error creating execution engine: " << ErrorMsg << "\n";
else
errs() << MODULE << "JIT not registered?\n";
return CL_EBYTECODE;
} |
daffb518 |
EE->RegisterJITEventListener(createOProfileJITEventListener()); |
2487a4a3 |
// Due to LLVM PR4816 only X86 supports non-lazy compilation, disable
// for now.
// EE->DisableLazyCompilation(); |
6b67ec6e |
EE->DisableSymbolSearching(); |
d1487222 |
|
ee8f1888 |
FunctionPassManager OurFPM(MP);
// Set up the optimizer pipeline. Start with registering info about how
// the target lays out data structures.
OurFPM.add(new TargetData(*EE->getTargetData()));
// Promote allocas to registers.
OurFPM.add(createPromoteMemoryToRegisterPass()); |
e6d1fe78 |
// Delete dead instructions
OurFPM.add(createDeadCodeEliminationPass()); |
ee8f1888 |
OurFPM.doInitialization(); |
e32caecb |
|
d0af4afe |
//TODO: create a wrapper that calls pthread_getspecific
const Type *HiddenCtx = PointerType::getUnqual(Type::getInt8Ty(bcs->engine->Context));
LLVMTypeMapper apiMap(bcs->engine->Context, cli_apicall_types, cli_apicall_maxtypes, HiddenCtx); |
e32caecb |
Function **apiFuncs = new Function *[cli_apicall_maxapi];
for (unsigned i=0;i<cli_apicall_maxapi;i++) {
const struct cli_apicall *api = &cli_apicalls[i];
const FunctionType *FTy = cast<FunctionType>(apiMap.get(69+api->type));
Function *F = Function::Create(FTy, Function::ExternalLinkage,
api->name, M);
void *dest;
switch (api->kind) {
case 0:
dest = (void*)cli_apicalls0[api->idx];
break;
case 1:
dest = (void*)cli_apicalls1[api->idx];
break;
}
EE->addGlobalMapping(F, dest);
apiFuncs[i] = F;
}
|
daffb518 |
for (unsigned i=0;i<bcs->count;i++) { |
3b33bd68 |
const struct cli_bc *bc = &bcs->all_bcs[i]; |
4789b8a5 |
if (bc->state == bc_skip)
continue; |
e32caecb |
LLVMCodegen Codegen(bc, M, bcs->engine->compiledFunctions, EE,
OurFPM, apiFuncs); |
ee8f1888 |
if (!Codegen.generate()) {
errs() << MODULE << "JIT codegen failed\n";
return CL_EBYTECODE;
} |
daffb518 |
}
|
85a25497 |
for (unsigned i=0;i<bcs->count;i++) {
bcs->all_bcs[i].state = bc_jit;
} |
d1487222 |
// compile all functions now, not lazily!
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
Function *Fn = &*I;
if (!Fn->isDeclaration())
EE->getPointerToFunction(Fn);
} |
e32caecb |
delete [] apiFuncs; |
d1487222 |
}
return -1;
} catch (std::bad_alloc &badalloc) {
errs() << MODULE << badalloc.what() << "\n";
return CL_EMEM;
} catch (...) {
errs() << MODULE << "Unexpected unknown exception occurred.\n";
return CL_EBYTECODE;
}
}
int bytecode_init(void)
{ |
df52b258 |
// If already initialized return
if (llvm_is_multithreaded())
return 0; |
d1487222 |
llvm_install_error_handler(llvm_error_handler);
sys::PrintStackTraceOnErrorSignal();
atexit(do_shutdown);
llvm_start_multithreaded();
// If we have a native target, initialize it to ensure it is linked in and
// usable by the JIT.
InitializeNativeTarget();
return 0;
}
// Called once when loading a new set of BC files
int cli_bytecode_init_jit(struct cli_all_bc *bcs)
{ |
6fa1a75b |
//TODO: if !llvm_is_multi... |
3b33bd68 |
bcs->engine = new(std::nothrow) struct cli_bcengine; |
d1487222 |
if (!bcs->engine)
return CL_EMEM;
return 0;
}
int cli_bytecode_done_jit(struct cli_all_bc *bcs)
{ |
2487a4a3 |
if (bcs->engine) {
if (bcs->engine->EE)
delete bcs->engine->EE;
delete bcs->engine;
bcs->engine = 0;
} |
d1487222 |
return 0;
} |
3b33bd68 |
void cli_bytecode_debug(int argc, char **argv)
{
cl::ParseCommandLineOptions(argc, argv);
} |
2487a4a3 |
int have_clamjit=1; |