f18dc607 |
/* |
e32caecb |
* JIT compile ClamAV bytecode. |
f18dc607 |
* |
c442ca9c |
* Copyright (C) 2013-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc. |
f18dc607 |
*
* 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" |
884a0b8f |
#include <pthread.h>
#ifndef _WIN32
#include <sys/time.h>
#endif |
9f8df4ae |
#include <cstdlib>
#include <csetjmp>
#include <new>
#include <cerrno>
#include <string> |
fb2b05cb |
|
1e30496d |
#include "ClamBCModule.h" |
daad92ac |
#include "ClamBCDiagnostics.h" |
9f8df4ae |
#include "llvm30_compat.h"
|
daffb518 |
#include "llvm/ADT/DenseMap.h" |
459b13ed |
#include "llvm/ADT/BitVector.h" |
99536a17 |
#include "llvm/ADT/PostOrderIterator.h" |
65c740d7 |
#include "llvm/ADT/StringMap.h" |
b3571ea9 |
#include "llvm/ADT/StringSwitch.h" |
6eeadbfe |
#include "llvm/ADT/Triple.h" |
99536a17 |
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ScalarEvolution.h" |
4a40b53a |
#if LLVM_VERSION < 35 |
99536a17 |
#include "llvm/Analysis/Verifier.h" |
744c40d1 |
#include "llvm/AutoUpgrade.h" |
4a40b53a |
#include "llvm/Support/TargetFolder.h"
#else
#include "llvm/IR/Verifier.h"
#include "llvm/IR/AutoUpgrade.h"
#include "llvm/Analysis/TargetFolder.h"
#endif |
3b33bd68 |
#include "llvm/ExecutionEngine/ExecutionEngine.h" |
2c859ec7 |
#if LLVM_VERSION < 36 |
3b33bd68 |
#include "llvm/ExecutionEngine/JIT.h" |
2c859ec7 |
#else
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Object/ObjectFile.h"
#endif |
3b33bd68 |
#include "llvm/ExecutionEngine/JITEventListener.h" |
ee8f1888 |
#include "llvm/PassManager.h" |
3b33bd68 |
#include "llvm/Support/Compiler.h" |
6b67ec6e |
#include "llvm/Support/Debug.h" |
3b33bd68 |
#include "llvm/Support/CommandLine.h" |
d1487222 |
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ManagedStatic.h" |
65c740d7 |
#include "llvm/Support/MemoryBuffer.h" |
d1487222 |
#include "llvm/Support/raw_ostream.h" |
65c740d7 |
#include "llvm/Support/SourceMgr.h" |
3b33bd68 |
#include "llvm/Support/PrettyStackTrace.h" |
0c79cc55 |
|
9f8df4ae |
#if LLVM_VERSION < 29
#include "llvm/System/DataTypes.h"
#include "llvm/System/Host.h"
#include "llvm/System/Memory.h"
#include "llvm/System/Mutex.h"
#include "llvm/System/Signals.h"
#include "llvm/System/Threading.h"
#include "llvm/System/ThreadLocal.h"
#else
#include "llvm/PassRegistry.h" |
0c79cc55 |
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/ThreadLocal.h" |
9f8df4ae |
#endif
|
34d8b8cf |
#if LLVM_VERSION < 33
#include "llvm/IntrinsicInst.h"
#else
#include "llvm/IR/IntrinsicInst.h"
#endif |
0c79cc55 |
#include "llvm/Support/Timer.h" |
dccafff0 |
|
ce238673 |
extern "C" { |
dccafff0 |
void LLVMInitializeX86AsmPrinter();
void LLVMInitializePowerPCAsmPrinter(); |
ce238673 |
} |
9f8df4ae |
#if LLVM_VERSION < 30 |
d1487222 |
#include "llvm/Target/TargetSelect.h" |
9f8df4ae |
#else
#include "llvm/Support/TargetSelect.h" |
59f1b78b |
#endif |
9f8df4ae |
|
d2171abf |
#include "llvm/Target/TargetOptions.h" |
6b67ec6e |
#include "llvm/Transforms/Scalar.h" |
6e52ce67 |
#include "llvm/Transforms/IPO.h" |
99536a17 |
#include "llvm/Transforms/Utils/BasicBlockUtils.h" |
d1487222 |
|
34d8b8cf |
#if LLVM_VERSION < 32
#include "llvm/Analysis/DebugInfo.h" |
4a40b53a |
#elif LLVM_VERSION < 35 |
34d8b8cf |
#include "llvm/DebugInfo.h" |
4a40b53a |
#else
#include "llvm/IR/DebugInfo.h" |
34d8b8cf |
#endif
#if LLVM_VERSION < 32
#include "llvm/Support/IRBuilder.h"
#include "llvm/Target/TargetData.h"
#elif LLVM_VERSION < 33
#include "llvm/IRBuilder.h"
#include "llvm/DataLayout.h"
#else
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/DataLayout.h"
#endif
#if LLVM_VERSION < 33
#include "llvm/CallingConv.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/LLVMContext.h"
#include "llvm/Intrinsics.h"
#include "llvm/Module.h"
#else
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Module.h"
#endif
|
9f8df4ae |
#if LLVM_VERSION < 34
#include "llvm/Support/CFG.h"
#else |
34d8b8cf |
#include "llvm/Analysis/CFG.h"
#endif
|
4a40b53a |
#if LLVM_VERSION >= 35
#include "llvm/IR/Dominators.h"
#endif
|
6e52ce67 |
//#define TIMING
#undef TIMING
|
2c7d5adc |
#include "llvm/Config/config.h" |
34d8b8cf |
#ifdef ENABLE_THREADS |
ee6ab4f8 |
#if !ENABLE_THREADS
#error "Thread support was explicitly disabled. Cannot continue" |
2c7d5adc |
#endif |
34d8b8cf |
#endif
#ifdef LLVM_ENABLE_THREADS
#if !LLVM_ENABLE_THREADS
#error "Thread support was explicitly disabled. Cannot continue"
#endif
#endif |
d38d6dad |
|
bcb354a6 |
#ifdef _GLIBCXX_PARALLEL
#error "libstdc++ parallel mode is not supported for ClamAV. Please remove -D_GLIBCXX_PARALLEL from CXXFLAGS!"
#endif
|
d38d6dad |
#ifdef HAVE_CONFIG_H
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION |
f60c59a4 |
#undef PACKAGE_URL |
d38d6dad |
#include "clamav-config.h"
#endif |
fb2b05cb |
#include <openssl/ssl.h>
#include <openssl/err.h>
|
f60c59a4 |
#include "dconf.h" |
d1487222 |
#include "clamav.h"
#include "clambc.h"
#include "bytecode.h" |
0a11015b |
#include "bytecode_priv.h" |
80b4b3fb |
#include "type_desc.h" |
d1487222 |
#define MODULE "libclamav JIT: "
|
9463f9fd |
extern "C" unsigned int cli_rndnum(unsigned int max); |
d1487222 |
using namespace llvm; |
daffb518 |
typedef DenseMap<const struct cli_bc_func*, void*> FunctionMapTy; |
d1487222 |
struct cli_bcengine {
ExecutionEngine *EE; |
e2752b2c |
JITEventListener *Listener; |
d1487222 |
LLVMContext Context; |
3b33bd68 |
FunctionMapTy compiledFunctions; |
9463f9fd |
union {
unsigned char b[16];
void* align;/* just to align field to ptr */
} guard; |
d1487222 |
};
|
09667cdd |
extern "C" uint8_t cli_debug_flag; |
9f8df4ae |
#if LLVM_VERSION >= 29 |
0c79cc55 |
namespace llvm {
void initializeRuntimeLimitsPass(PassRegistry&);
};
#endif |
d1487222 |
namespace {
|
9f8df4ae |
#if LLVM_VERSION >= 28 |
e3a54503 |
#define llvm_report_error(x) report_fatal_error(x)
#define llvm_install_error_handler(x) install_fatal_error_handler(x)
#define DwarfExceptionHandling JITExceptionHandling
#define SetCurrentDebugLocation(x) SetCurrentDebugLocation(DebugLoc::getFromDILocation(x)) |
1755adf0 |
#define DEFINEPASS(passname) passname() : FunctionPass(ID)
#else
#define DEFINEPASS(passname) passname() : FunctionPass(&ID) |
e3a54503 |
#endif
|
9f8df4ae |
#if LLVM_VERSION >= 29 |
0c79cc55 |
#define NORETURN LLVM_ATTRIBUTE_NORETURN
#endif
|
6b67ec6e |
static sys::ThreadLocal<const jmp_buf> ExceptionReturn;
|
744c40d1 |
static void UpgradeCall(CallInst *&C, Function *Intr)
{
Function *New;
if (!UpgradeIntrinsicFunction(Intr, New) || New == Intr)
return;
UpgradeIntrinsicCall(C, New);
}
|
162c2e43 |
extern "C" {
#ifdef __GNUC__
void cli_errmsg(const char *str, ...) __attribute__((format(printf, 1, 2)));
#else
void cli_errmsg(const char *str, ...);
#endif
#ifdef __GNUC__
void cli_warnmsg(const char *str, ...) __attribute__((format(printf, 1, 2)));
#else
void cli_warnmsg(const char *str, ...);
#endif
#ifdef __GNUC__
void cli_dbgmsg_internal(const char *str, ...) __attribute__((format(printf, 1, 2)));
#else
void cli_dbgmsg_internal(const char *str, ...);
#endif
}
class ScopedExceptionHandler {
public: |
3a35520b |
jmp_buf &getEnv() { return env;}
void Set() {
/* set the exception handler's return location to here for the
* current thread */
ExceptionReturn.set((const jmp_buf*)&env); |
162c2e43 |
}
~ScopedExceptionHandler() {
/* leaving scope, remove exception handler for current thread */
ExceptionReturn.erase();
}
private:
jmp_buf env;
}; |
3a35520b |
#define HANDLER_TRY(handler) \
if (setjmp(handler.getEnv()) == 0) {\
handler.Set();
#define HANDLER_END(handler) \
} else cli_warnmsg("[Bytecode JIT]: recovered from error\n");
|
162c2e43 |
|
3b33bd68 |
void do_shutdown() { |
162c2e43 |
ScopedExceptionHandler handler; |
3a35520b |
HANDLER_TRY(handler) { |
162c2e43 |
// TODO: be on the safe side, and clear errors here,
// otherwise destructor calls report_fatal_error
((class raw_fd_ostream&)errs()).clear_error();
llvm_shutdown();
((class raw_fd_ostream&)errs()).clear_error();
} |
3a35520b |
HANDLER_END(handler); |
f62d5398 |
remove_fatal_error_handler(); |
3b33bd68 |
} |
daffb518 |
|
6b67ec6e |
static void NORETURN jit_exception_handler(void)
{ |
ce238673 |
jmp_buf* buf = const_cast<jmp_buf*>(ExceptionReturn.get()); |
162c2e43 |
if (buf) {
// For errors raised during bytecode generation and execution.
longjmp(*buf, 1);
} else {
// Oops, got no error recovery pointer set up,
// this is probably an error raised during shutdown.
cli_errmsg("[Bytecode JIT]: exception handler called, but no recovery point set up"); |
f62d5398 |
// should never happen, we remove the error handler when we don't use
// LLVM anymore, and when we use it, we do set an error recovery point. |
ce238673 |
llvm_unreachable("Bytecode JIT]: no exception handler recovery installed, but exception hit!"); |
162c2e43 |
} |
6b67ec6e |
}
|
9463f9fd |
static void NORETURN jit_ssp_handler(void)
{ |
162c2e43 |
cli_errmsg("[Bytecode JIT]: *** stack smashing detected, bytecode aborted\n"); |
9463f9fd |
jit_exception_handler();
}
|
34d8b8cf |
#if LLVM_VERSION < 33 |
3b33bd68 |
void llvm_error_handler(void *user_data, const std::string &reason) |
34d8b8cf |
#else
void llvm_error_handler(void *user_data, const std::string &reason, bool gen_crash_diag = true)
#endif |
3b33bd68 |
{ |
80b4b3fb |
// Output it to stderr, it might exceed the 1k/4k limit of cli_errmsg |
162c2e43 |
cli_errmsg("[Bytecode JIT]: [LLVM error] %s\n", reason.c_str()); |
6b67ec6e |
jit_exception_handler(); |
3b33bd68 |
} |
daffb518 |
|
b3571ea9 |
// Since libgcc is not available on all compilers (for example on win32),
// just define what these functions should do, the compiler will forward to
// the appropriate libcall if needed.
static int64_t rtlib_sdiv_i64(int64_t a, int64_t b)
{
return a/b;
}
static uint64_t rtlib_udiv_i64(uint64_t a, uint64_t b)
{
return a/b;
}
static int64_t rtlib_srem_i64(int64_t a, int64_t b)
{
return a%b;
}
static uint64_t rtlib_urem_i64(uint64_t a, uint64_t b)
{
return a%b;
}
static int64_t rtlib_mul_i64(uint64_t a, uint64_t b)
{
return a*b;
}
static int64_t rtlib_shl_i64(int64_t a, int32_t b)
{
return a << b;
}
static int64_t rtlib_srl_i64(int64_t a, int32_t b)
{
return (uint64_t)a >> b;
}
/* Implementation independent sign-extended signed right shift */
#ifdef HAVE_SAR
#define CLI_SRS(n,s) ((n)>>(s))
#else
#define CLI_SRS(n,s) ((((n)>>(s)) ^ (1<<(sizeof(n)*8-1-s))) - (1<<(sizeof(n)*8-1-s)))
#endif
static int64_t rtlib_sra_i64(int64_t a, int32_t b)
{
return CLI_SRS(a, b);//CLI_./..
}
|
cb1260cc |
static void rtlib_bzero(void *s, size_t n)
{
memset(s, 0, n);
}
|
49558860 |
#ifdef _WIN32 |
e92dda94 |
#ifdef _WIN64
extern "C" void __chkstk(void);
#else |
49558860 |
extern "C" void _chkstk(void);
#endif |
e92dda94 |
#endif |
b3571ea9 |
// Resolve integer libcalls, but nothing else.
static void* noUnknownFunctions(const std::string& name) {
void *addr =
StringSwitch<void*>(name)
.Case("__divdi3", (void*)(intptr_t)rtlib_sdiv_i64)
.Case("__udivdi3", (void*)(intptr_t)rtlib_udiv_i64)
.Case("__moddi3", (void*)(intptr_t)rtlib_srem_i64)
.Case("__umoddi3", (void*)(intptr_t)rtlib_urem_i64)
.Case("__muldi3", (void*)(intptr_t)rtlib_mul_i64)
.Case("__ashrdi3", (void*)(intptr_t)rtlib_sra_i64)
.Case("__ashldi3", (void*)(intptr_t)rtlib_shl_i64)
.Case("__lshrdi3", (void*)(intptr_t)rtlib_srl_i64) |
cb1260cc |
.Case("__bzero", (void*)(intptr_t)rtlib_bzero) |
4395bb9a |
.Case("memmove", (void*)(intptr_t)memmove)
.Case("memcpy", (void*)(intptr_t)memcpy)
.Case("memset", (void*)(intptr_t)memset) |
1e30496d |
.Case("abort", (void*)(intptr_t)jit_exception_handler) |
49558860 |
#ifdef _WIN32 |
ca3e6c58 |
#ifdef _WIN64 |
3d8ca9f6 |
.Case("_chkstk", (void*)(intptr_t)__chkstk) |
ca3e6c58 |
#else |
49558860 |
.Case("_chkstk", (void*)(intptr_t)_chkstk)
#endif |
ca3e6c58 |
#endif |
b3571ea9 |
.Default(0);
if (addr)
return addr;
|
2c859ec7 |
#if LLVM_VERSION < 36 |
b3571ea9 |
std::string reason((Twine("Attempt to call external function ")+name).str());
llvm_error_handler(0, reason); |
2c859ec7 |
#else
// noUnknownFunctions relies on addGlobalMapping, which doesn't work with MCJIT.
// Now the function pointers are found with SymbolSearching.
#endif |
b3571ea9 |
return 0;
}
|
09667cdd |
class NotifyListener : public JITEventListener {
public: |
2c859ec7 |
#if LLVM_VERSION < 36 |
09667cdd |
virtual void NotifyFunctionEmitted(const Function &F,
void *Code, size_t Size,
const EmittedFunctionDetails &Details)
{
if (!cli_debug_flag)
return; |
29553b8e |
cli_dbgmsg_internal("[Bytecode JIT]: emitted function %s of %ld bytes at %p\n", |
34d8b8cf |
#if LLVM_VERSION < 31 |
29553b8e |
F.getNameStr().c_str(), (long)Size, Code); |
34d8b8cf |
#else
F.getName().str().c_str(), (long)Size, Code);
#endif |
09667cdd |
} |
2c859ec7 |
#else
// MCJIT doesn't emit single functions, but instead whole objects.
virtual void NotifyObjectEmitted(const object::ObjectFile &Obj,
const RuntimeDyld::LoadedObjectInfo &L)
{
if (!cli_debug_flag)
return;
cli_dbgmsg_internal("[Bytecode JIT]; emitted %s %s of %zd bytes\n",
Obj.getFileFormatName().str().c_str(),
Obj.getFileName().str().c_str(), Obj.getData().size());
}
#endif |
09667cdd |
};
|
c0a306b2 |
class TimerWrapper {
private:
Timer *t;
public:
TimerWrapper(const std::string &name) {
t = 0;
#ifdef TIMING
t = new Timer(name);
#endif
}
~TimerWrapper()
{
if (t)
delete t;
}
void startTimer()
{
if (t)
t->startTimer();
}
void stopTimer()
{
if (t)
t->stopTimer();
}
};
|
e32caecb |
class LLVMTypeMapper { |
3b33bd68 |
private: |
9f8df4ae |
#if LLVM_VERSION < 30 |
e32caecb |
std::vector<PATypeHolder> TypeMap; |
9f8df4ae |
#else
std::vector<Type*> TypeMap; |
59f1b78b |
#endif |
3b33bd68 |
LLVMContext &Context; |
e32caecb |
unsigned numTypes; |
59f1b78b |
constType *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: |
c0a306b2 |
TimerWrapper pmTimer;
TimerWrapper irgenTimer; |
6e52ce67 |
|
e32caecb |
LLVMTypeMapper(LLVMContext &Context, const struct cli_bc_type *types, |
59f1b78b |
unsigned count, constType *Hidden=0) : Context(Context), numTypes(count), |
6e52ce67 |
pmTimer("Function passes"),irgenTimer("IR generation") |
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++) { |
9f8df4ae |
#if LLVM_VERSION < 30 |
e32caecb |
TypeMap.push_back(OpaqueType::get(Context)); |
9f8df4ae |
#else
TypeMap.push_back(0); |
59f1b78b |
#endif |
e32caecb |
}
for (unsigned i=0;i<count;i++) {
const struct cli_bc_type *type = &types[i]; |
59f1b78b |
constType *Ty = buildType(type, types, Hidden, 0); |
9f8df4ae |
#if LLVM_VERSION < 30 |
59f1b78b |
// Make the opaque type a concrete type, doing recursive type
// unification if needed.
cast<OpaqueType>(TypeMap[i].get())->refineAbstractTypeTo(Ty); |
9f8df4ae |
#else
TypeMap[i] = Ty; |
59f1b78b |
#endif
}
}
constType *buildType(const struct cli_bc_type *type, const struct cli_bc_type *types, constType *Hidden,
int recursive)
{
std::vector<constType*> Elts;
unsigned n = type->kind == DArrayType ? 1 : type->numElements;
for (unsigned j=0;j<n;j++) {
Elts.push_back(get(type->containedTypes[j], types, Hidden));
}
constType *Ty;
switch (type->kind) {
case DFunctionType: |
e32caecb |
{
assert(Elts.size() > 0 && "Function with no return type?"); |
59f1b78b |
constType *RetTy = Elts[0]; |
d0af4afe |
if (Hidden)
Elts[0] = Hidden;
else
Elts.erase(Elts.begin()); |
e32caecb |
Ty = FunctionType::get(RetTy, Elts, false);
break;
} |
59f1b78b |
case DPointerType:
if (!PointerType::isValidElementType(Elts[0]))
Ty = PointerType::getUnqual(Type::getInt8Ty(Context));
else
Ty = PointerType::getUnqual(Elts[0]);
break;
case DStructType:
case DPackedStructType:
Ty = StructType::get(Context, Elts, type->kind == DPackedStructType);
break;
case DArrayType:
Ty = ArrayType::get(Elts[0], type->numElements);
break;
default:
llvm_unreachable("type->kind"); |
e32caecb |
} |
59f1b78b |
return Ty; |
e32caecb |
}
|
59f1b78b |
constType *get(uint16_t ty, const struct cli_bc_type *types, constType *Hidden) |
e32caecb |
{ |
a45e2577 |
ty &= 0x7fff; |
e32caecb |
if (ty < 69)
return getStatic(ty); |
3b33bd68 |
ty -= 69; |
e32caecb |
assert(ty < numTypes && "TypeID out of range"); |
9f8df4ae |
#if LLVM_VERSION < 30
return TypeMap[ty].get();
#else |
59f1b78b |
Type *Ty = TypeMap[ty];
if (Ty)
return Ty;
assert(types && Hidden || "accessing not-yet-built type");
Ty = buildType(&types[ty], types, Hidden, 1);
TypeMap[ty] = Ty;
return Ty;
#endif |
3b33bd68 |
} |
e32caecb |
}; |
3b33bd68 |
|
57bbb2eb |
struct CommonFunctions {
Function *FHandler;
Function *FMemset;
Function *FMemmove;
Function *FMemcpy;
Function *FRealmemset;
Function *FRealMemmove;
Function *FRealmemcmp;
Function *FRealmemcpy;
Function *FBSwap16;
Function *FBSwap32;
Function *FBSwap64;
}; |
daffb518 |
|
99536a17 |
// loops with tripcounts higher than this need timeout check
static const unsigned LoopThreshold = 1000;
// after every N API calls we need timeout check
static const unsigned ApiThreshold = 100;
class RuntimeLimits : public FunctionPass {
typedef SmallVector<std::pair<const BasicBlock*, const BasicBlock*>, 16>
BBPairVectorTy;
typedef SmallSet<BasicBlock*, 16> BBSetTy;
typedef DenseMap<const BasicBlock*, unsigned> BBMapTy;
bool loopNeedsTimeoutCheck(ScalarEvolution &SE, const Loop *L, BBMapTy &Map) {
// This BB is a loop header, if trip count is small enough
// no timeout checks are needed here.
const SCEV *S = SE.getMaxBackedgeTakenCount(L);
if (isa<SCEVCouldNotCompute>(S))
return true;
DEBUG(errs() << "Found loop trip count" << *S << "\n");
ConstantRange CR = SE.getUnsignedRange(S);
uint64_t max = CR.getUnsignedMax().getLimitedValue();
DEBUG(errs() << "Found max trip count " << max << "\n");
if (max > LoopThreshold)
return true;
unsigned apicalls = 0;
for (Loop::block_iterator J=L->block_begin(),JE=L->block_end();
J != JE; ++J) {
apicalls += Map[*J];
}
apicalls *= max;
if (apicalls > ApiThreshold) {
DEBUG(errs() << "apicall threshold exceeded: " << apicalls << "\n");
return true;
}
Map[L->getHeader()] = apicalls;
return false;
}
public:
static char ID; |
0c79cc55 |
DEFINEPASS(RuntimeLimits) { |
9f8df4ae |
#if LLVM_VERSION >= 29 |
0c79cc55 |
PassRegistry &Registry = *PassRegistry::getPassRegistry();
initializeRuntimeLimitsPass(Registry);
#endif
} |
99536a17 |
virtual bool runOnFunction(Function &F) {
BBSetTy BackedgeTargets;
if (!F.isDeclaration()) {
// Get the common backedge targets.
// Note that we don't rely on LoopInfo here, since
// it is possible to construct a CFG that doesn't have natural loops,
// yet it does have backedges, and thus can lead to unbounded/high
// execution time.
BBPairVectorTy V;
FindFunctionBackedges(F, V);
for (BBPairVectorTy::iterator I=V.begin(),E=V.end();I != E; ++I) {
BackedgeTargets.insert(const_cast<BasicBlock*>(I->second));
}
}
BBSetTy needsTimeoutCheck;
BBMapTy BBMap; |
4a40b53a |
#if LLVM_VERSION < 35 |
99536a17 |
DominatorTree &DT = getAnalysis<DominatorTree>(); |
4a40b53a |
#else
DominatorTree &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
#endif |
99536a17 |
for (Function::iterator I=F.begin(),E=F.end(); I != E; ++I) {
BasicBlock *BB = &*I;
unsigned apicalls = 0;
for (BasicBlock::const_iterator J=BB->begin(),JE=BB->end();
J != JE; ++J) {
if (const CallInst *CI = dyn_cast<CallInst>(J)) {
Function *F = CI->getCalledFunction();
if (!F || F->isDeclaration())
apicalls++;
}
}
if (apicalls > ApiThreshold) {
DEBUG(errs() << "apicall threshold exceeded: " << apicalls << "\n");
needsTimeoutCheck.insert(BB);
apicalls = 0;
}
BBMap[BB] = apicalls;
}
if (!BackedgeTargets.empty()) {
LoopInfo &LI = getAnalysis<LoopInfo>();
ScalarEvolution &SE = getAnalysis<ScalarEvolution>();
// Now check whether any of these backedge targets are part of a loop
// with a small constant trip count
for (BBSetTy::iterator I=BackedgeTargets.begin(),E=BackedgeTargets.end();
I != E; ++I) {
const Loop *L = LI.getLoopFor(*I);
if (L && L->getHeader() == *I &&
!loopNeedsTimeoutCheck(SE, L, BBMap))
continue;
needsTimeoutCheck.insert(*I);
BBMap[*I] = 0;
}
}
// Estimate number of apicalls by walking dominator-tree bottom-up.
// BBs that have timeout checks are considered to have 0 APIcalls
// (since we already checked for timeout).
for (po_iterator<DomTreeNode*> I = po_begin(DT.getRootNode()),
E = po_end(DT.getRootNode()); I != E; ++I) {
if (needsTimeoutCheck.count(I->getBlock()))
continue;
unsigned apicalls = BBMap[I->getBlock()];
for (DomTreeNode::iterator J=I->begin(),JE=I->end();
J != JE; ++J) {
apicalls += BBMap[(*J)->getBlock()];
}
if (apicalls > ApiThreshold) {
needsTimeoutCheck.insert(I->getBlock());
apicalls = 0;
}
BBMap[I->getBlock()] = apicalls;
}
if (needsTimeoutCheck.empty())
return false;
DEBUG(errs() << "needs timeoutcheck:\n"); |
59f1b78b |
std::vector<constType*>args; |
99536a17 |
FunctionType* abrtTy = FunctionType::get(
Type::getVoidTy(F.getContext()),args,false);
Constant *func_abort =
F.getParent()->getOrInsertFunction("abort", abrtTy);
BasicBlock *AbrtBB = BasicBlock::Create(F.getContext(), "", &F);
CallInst* AbrtC = CallInst::Create(func_abort, "", AbrtBB);
AbrtC->setCallingConv(CallingConv::C);
AbrtC->setTailCall(true); |
34d8b8cf |
#if LLVM_VERSION < 32 |
99536a17 |
AbrtC->setDoesNotReturn(true);
AbrtC->setDoesNotThrow(true); |
34d8b8cf |
#else
AbrtC->setDoesNotReturn();
AbrtC->setDoesNotThrow();
#endif |
99536a17 |
new UnreachableInst(F.getContext(), AbrtBB);
IRBuilder<false> Builder(F.getContext()); |
872d0a90 |
Value *Flag = F.arg_begin(); |
9f8df4ae |
#if LLVM_VERSION < 30 |
99536a17 |
Function *LSBarrier = Intrinsic::getDeclaration(F.getParent(),
Intrinsic::memory_barrier);
Value *MBArgs[] = {
ConstantInt::getFalse(F.getContext()),
ConstantInt::getFalse(F.getContext()),
ConstantInt::getTrue(F.getContext()),
ConstantInt::getFalse(F.getContext()),
ConstantInt::getFalse(F.getContext())
}; |
872d0a90 |
#endif |
99536a17 |
verifyFunction(F);
BasicBlock *BB = &F.getEntryBlock();
Builder.SetInsertPoint(BB, BB->getTerminator());
Flag = Builder.CreatePointerCast(Flag, PointerType::getUnqual(
Type::getInt1Ty(F.getContext())));
for (BBSetTy::iterator I=needsTimeoutCheck.begin(),
E=needsTimeoutCheck.end(); I != E; ++I) {
BasicBlock *BB = *I;
Builder.SetInsertPoint(BB, BB->getTerminator()); |
9f8df4ae |
#if LLVM_VERSION < 30 |
99536a17 |
// store-load barrier: will be a no-op on x86 but not other arches |
59f1b78b |
Builder.CreateCall(LSBarrier, ARRAYREF(Value*, MBArgs, MBArgs+5)); |
9f8df4ae |
#else
Builder.CreateFence(Release); |
872d0a90 |
#endif |
99536a17 |
// Load Flag that tells us we timed out (first byte in bc_ctx)
Value *Cond = Builder.CreateLoad(Flag, true);
BasicBlock *newBB = SplitBlock(BB, BB->getTerminator(), this);
TerminatorInst *TI = BB->getTerminator();
BranchInst::Create(AbrtBB, newBB, Cond, TI);
TI->eraseFromParent();
// Update dominator info
DomTreeNode *N = DT.getNode(AbrtBB);
if (!N) {
DT.addNewBlock(AbrtBB, BB);
} else {
BasicBlock *DomBB = DT.findNearestCommonDominator(BB,
N->getIDom()->getBlock());
DT.changeImmediateDominator(AbrtBB, DomBB);
}
DEBUG(errs() << *I << "\n");
} |
bdd9aeae |
//verifyFunction(F); |
99536a17 |
return true;
}
virtual void getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesAll();
AU.addRequired<LoopInfo>();
AU.addRequired<ScalarEvolution>(); |
4a40b53a |
#if LLVM_VERSION < 35 |
99536a17 |
AU.addRequired<DominatorTree>(); |
4a40b53a |
#else
AU.addRequired<DominatorTreeWrapperPass>();
#endif |
99536a17 |
}
};
char RuntimeLimits::ID;
|
6e52ce67 |
// select i1 false ... which instcombine would simplify but we don't run
// instcombine.
class BrSimplifier : public FunctionPass {
public:
static char ID; |
1755adf0 |
DEFINEPASS(BrSimplifier) {} |
6e52ce67 |
virtual bool runOnFunction(Function &F) {
bool Changed = false;
for (Function::iterator I=F.begin(),E=F.end(); I != E; ++I) {
if (BranchInst *BI = dyn_cast<BranchInst>(I->getTerminator())) {
if (BI->isUnconditional())
continue;
Value *V = BI->getCondition();
if (ConstantInt *CI = dyn_cast<ConstantInt>(V)) {
BasicBlock *Other;
if (CI->isOne()) {
BranchInst::Create(BI->getSuccessor(0), &*I);
Other = BI->getSuccessor(1);
} else {
BranchInst::Create(BI->getSuccessor(1), &*I);
Other = BI->getSuccessor(0);
}
Other->removePredecessor(&*I);
BI->eraseFromParent();
Changed = true;
}
}
for (BasicBlock::iterator J=I->begin(),JE=I->end();
J != JE;) {
SelectInst *SI = dyn_cast<SelectInst>(J);
++J;
if (!SI)
continue;
ConstantInt *CI = dyn_cast<ConstantInt>(SI->getCondition());
if (!CI)
continue;
if (CI->isOne())
SI->replaceAllUsesWith(SI->getTrueValue());
else
SI->replaceAllUsesWith(SI->getFalseValue());
SI->eraseFromParent();
Changed = true;
}
}
return Changed;
}
};
char BrSimplifier::ID;
/*
class SimpleGlobalDCE : public ModulePass {
ExecutionEngine *EE;
public:
static char ID;
SimpleGlobalDCE(ExecutionEngine *EE) : ModulePass(&ID), EE(EE) {}
virtual bool runOnModule(Module &M) {
bool Changed = false;
std::vector<GlobalValue*> toErase;
for (Module::global_iterator I = M.global_begin(), E = M.global_end(); I != E; ++I) {
GlobalValue *GV = &*I;
if (GV->use_empty() && !EE->getPointerToGlobalIfAvailable(GV))
toErase.push_back(GV);
}
for (std::vector<GlobalValue*>::iterator I=toErase.begin(), E=toErase.end();
I != E; ++I) {
(*I)->eraseFromParent();
Changed = true;
}
return Changed;
}
};
char SimpleGlobalDCE::ID;
*/ |
e3a54503 |
class LLVMCodegen { |
e32caecb |
private:
const struct cli_bc *bc;
Module *M;
LLVMContext &Context; |
3cd48316 |
ExecutionEngine *EE; |
6e52ce67 |
FunctionPassManager &PM, &PMUnsigned; |
e32caecb |
LLVMTypeMapper *TypeMap; |
3cd48316 |
|
e32caecb |
Function **apiFuncs; |
9fac78c7 |
LLVMTypeMapper &apiMap; |
3cd48316 |
FunctionMapTy &compiledFunctions; |
e32caecb |
Twine BytecodeID; |
3cd48316 |
|
e32caecb |
TargetFolder Folder;
IRBuilder<false, TargetFolder> Builder; |
3cd48316 |
|
459b13ed |
std::vector<Value*> globals; |
3cd48316 |
DenseMap<unsigned, unsigned> GVoffsetMap; |
59f1b78b |
DenseMap<unsigned, constType*> GVtypeMap; |
e32caecb |
Value **Values;
unsigned numLocals;
unsigned numArgs; |
53bd5bb1 |
std::vector<MDNode*> mdnodes; |
3b33bd68 |
|
57bbb2eb |
struct CommonFunctions *CF;
|
59f1b78b |
Value *getOperand(const struct cli_bc_func *func, constType *Ty, operand_t operand) |
e6d1fe78 |
{
unsigned map[] = {0, 1, 2, 3, 3, 4, 4, 4, 4};
if (operand < func->numValues)
return Values[operand]; |
09667cdd |
unsigned w = Ty->getPrimitiveSizeInBits();
if (w > 1)
w = (w+7)/8;
else
w = 0; |
e6d1fe78 |
return convertOperand(func, map[w], operand);
}
|
59f1b78b |
Value *convertOperand(const struct cli_bc_func *func, constType *Ty, operand_t operand) |
ee8f1888 |
{
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]; |
a45e2577 |
if (func->types[operand]&0x8000 && V->getType() == Ty) { |
e6d1fe78 |
return V; |
a45e2577 |
}
V = Builder.CreateLoad(V); |
688799d1 |
if (V->getType() != Ty &&
isa<PointerType>(V->getType()) &&
isa<PointerType>(Ty))
V = Builder.CreateBitCast(V, Ty); |
322a0ea6 |
if (V->getType() != Ty) { |
29553b8e |
if (cli_debug_flag) {
std::string str;
raw_string_ostream ostr(str);
ostr << operand << " " ;
V->print(ostr);
Ty->print(ostr); |
b6187b63 |
M->dump();
cli_dbgmsg_internal("[Bytecode JIT]: operand %d: %s\n", operand,ostr.str().c_str()); |
29553b8e |
} |
322a0ea6 |
llvm_report_error("(libclamav) Type mismatch converting operand");
} |
a45e2577 |
return V; |
e6d1fe78 |
} |
09667cdd |
unsigned w = Ty->getPrimitiveSizeInBits();
if (w > 1)
w = (w+7)/8;
else
w = 0; |
ee8f1888 |
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]; |
a45e2577 |
if (operand < func->numValues) {
if (func->types[operand]&0x8000)
return Values[operand]; |
ee8f1888 |
return Builder.CreateLoad(Values[operand]); |
a45e2577 |
} |
ee8f1888 |
|
ec077929 |
if (operand & 0x80000000) {
operand &= 0x7fffffff;
assert(operand < globals.size() && "Global index out of range");
// Global |
65c740d7 |
if (!operand)
return ConstantPointerNull::get(PointerType::getUnqual(Type::getInt8Ty(Context))); |
124af5e8 |
assert(globals[operand]); |
459b13ed |
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(globals[operand])) {
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(GV->getInitializer())) {
return CE;
}
return GV; |
7189addb |
} |
459b13ed |
return globals[operand]; |
ec077929 |
} |
ee8f1888 |
// 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; |
59f1b78b |
constType *Ty; |
ee8f1888 |
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; |
3cd48316 |
default:
llvm_unreachable("width"); |
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 |
|
57bbb2eb |
// Insert code that calls \arg CF->FHandler if \arg FailCond is true. |
6b67ec6e |
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 |
|
59f1b78b |
constType* mapType(uint16_t typeID) |
e32caecb |
{ |
59f1b78b |
return TypeMap->get(typeID&0x7fffffff, NULL, NULL); |
e32caecb |
} |
ec077929 |
|
59f1b78b |
Constant *buildConstant(constType *Ty, uint64_t *components, unsigned &c) |
ec077929 |
{ |
59f1b78b |
if (constPointerType *PTy = dyn_cast<PointerType>(Ty)) { |
236fb136 |
Value *idxs[1] = {
ConstantInt::get(Type::getInt64Ty(Context), components[c++]) |
7189addb |
}; |
459b13ed |
unsigned idx = components[c++]; |
65c740d7 |
if (!idx)
return ConstantPointerNull::get(PTy); |
459b13ed |
assert(idx < globals.size());
GlobalVariable *GV = cast<GlobalVariable>(globals[idx]); |
59f1b78b |
Type *IP8Ty = PointerType::getUnqual(Type::getInt8Ty(Ty->getContext())); |
236fb136 |
Constant *C = ConstantExpr::getPointerCast(GV, IP8Ty);
//TODO: check constant bounds here |
7945ff1e |
return ConstantExpr::getPointerCast( |
59f1b78b |
ConstantExpr::getInBoundsGetElementPtr(C, ARRAYREF(Value*, idxs, 1)), |
7945ff1e |
PTy); |
7189addb |
} |
ec077929 |
if (isa<IntegerType>(Ty)) {
return ConstantInt::get(Ty, components[c++]);
} |
59f1b78b |
if (constArrayType *ATy = dyn_cast<ArrayType>(Ty)) { |
ec077929 |
std::vector<Constant*> elements;
elements.reserve(ATy->getNumElements());
for (unsigned i=0;i<ATy->getNumElements();i++) {
elements.push_back(buildConstant(ATy->getElementType(), components, c));
}
return ConstantArray::get(ATy, elements);
} |
59f1b78b |
if (constStructType *STy = dyn_cast<StructType>(Ty)) { |
ec077929 |
std::vector<Constant*> elements;
elements.reserve(STy->getNumElements());
for (unsigned i=0;i<STy->getNumElements();i++) {
elements.push_back(buildConstant(STy->getElementType(i), components, c));
}
return ConstantStruct::get(STy, elements);
}
Ty->dump(); |
3cd48316 |
llvm_unreachable("invalid type"); |
ec077929 |
return 0;
}
|
3b33bd68 |
public: |
57bbb2eb |
LLVMCodegen(const struct cli_bc *bc, Module *M, struct CommonFunctions *CF, FunctionMapTy &cFuncs, |
6e52ce67 |
ExecutionEngine *EE, FunctionPassManager &PM, FunctionPassManager &PMUnsigned, |
9fac78c7 |
Function **apiFuncs, LLVMTypeMapper &apiMap) |
3cd48316 |
: bc(bc), M(M), Context(M->getContext()), EE(EE), |
3609e2d7 |
PM(PM),PMUnsigned(PMUnsigned), TypeMap(), apiFuncs(apiFuncs),apiMap(apiMap), |
3cd48316 |
compiledFunctions(cFuncs), BytecodeID("bc"+Twine(bc->id)), |
34d8b8cf |
#if LLVM_VERSION < 32 |
3609e2d7 |
Folder(EE->getTargetData()), Builder(Context, Folder), Values(), CF(CF) { |
34d8b8cf |
#else
Folder(EE->getDataLayout()), Builder(Context, Folder), Values(), CF(CF) {
#endif |
3cd48316 |
|
9fac78c7 |
for (unsigned i=0;i<cli_apicall_maxglobal - _FIRST_GLOBAL;i++) { |
80b4b3fb |
unsigned id = cli_globals[i].globalid;
GVoffsetMap[id] = cli_globals[i].offset;
} |
3609e2d7 |
numLocals = 0;
numArgs = 0; |
80b4b3fb |
} |
daffb518 |
|
9f8df4ae |
#if LLVM_VERSION < 30 |
a45e2577 |
template <typename InputIterator> |
59f1b78b |
#endif
Value* createGEP(Value *Base, constType *ETy, ARRAYREFPARAM(Value*,InputIterator Start,InputIterator End,ARef)) {
constType *Ty = GetElementPtrInst::getIndexedType(Base->getType(),ARRAYREFP(Start,End,ARef)); |
322a0ea6 |
if (!Ty || (ETy && (Ty != ETy && (!isa<IntegerType>(Ty) || !isa<IntegerType>(ETy))))) { |
29553b8e |
if (cli_debug_flag) {
std::string str;
raw_string_ostream ostr(str);
ostr << "Wrong indices for GEP opcode: "
<< " expected type: " << *ETy;
if (Ty)
ostr << " actual type: " << *Ty;
ostr << " base: " << *Base << ";";
Base->getType()->print(ostr);
ostr << "\n indices: "; |
9f8df4ae |
#if LLVM_VERSION < 30 |
29553b8e |
for (InputIterator I=Start; I != End; I++) { |
9f8df4ae |
#else
for (ArrayRef<Value*>::iterator I=ARef.begin(); I != ARef.end(); I++) { |
59f1b78b |
#endif |
29553b8e |
ostr << **I << ", ";
}
ostr << "\n";
cli_dbgmsg_internal("[Bytecode JIT]: %s\n", ostr.str().c_str());
} else {
cli_warnmsg("[Bytecode JIT]: Wrong indices for GEP opcode\n"); |
a45e2577 |
} |
322a0ea6 |
return 0; |
a45e2577 |
} |
59f1b78b |
return Builder.CreateGEP(Base,ARRAYREFP(Start,End,ARef)); |
322a0ea6 |
}
|
9f8df4ae |
#if LLVM_VERSION < 30 |
322a0ea6 |
template <typename InputIterator> |
59f1b78b |
#endif
bool createGEP(unsigned dest, Value *Base, ARRAYREFPARAM(Value*,InputIterator Start,InputIterator End,ARef)) { |
322a0ea6 |
assert(dest >= numArgs && dest < numLocals+numArgs && "Instruction destination out of range"); |
59f1b78b |
constType *ETy = cast<PointerType>(cast<PointerType>(Values[dest]->getType())->getElementType())->getElementType();
Value *V = createGEP(Base, ETy, ARRAYREFP(Start,End,ARef)); |
688799d1 |
if (!V) { |
29553b8e |
if (cli_debug_flag)
cli_dbgmsg_internal("[Bytecode JIT] @%d\n", dest); |
322a0ea6 |
return false; |
688799d1 |
} |
322a0ea6 |
V = Builder.CreateBitCast(V, PointerType::getUnqual(ETy)); |
a45e2577 |
Store(dest, V);
return true;
}
|
53bd5bb1 |
MDNode *convertMDNode(unsigned i) {
if (i < mdnodes.size()) {
if (mdnodes[i])
return mdnodes[i];
} else
mdnodes.resize(i+1);
assert(i < mdnodes.size());
const struct cli_bc_dbgnode *node = &bc->dbgnodes[i]; |
2c859ec7 |
#if LLVM_VERSION < 36 |
53bd5bb1 |
Value **Vals = new Value*[node->numelements]; |
2c859ec7 |
#else
Metadata **Vals = new Metadata*[node->numelements];
#endif |
53bd5bb1 |
for (unsigned j=0;j<node->numelements;j++) {
const struct cli_bc_dbgnode_element* el = &node->elements[j]; |
2c859ec7 |
#if LLVM_VERSION < 36 |
53bd5bb1 |
Value *V; |
2c859ec7 |
#else
Metadata *V;
#endif |
53bd5bb1 |
if (!el->len) {
if (el->nodeid == ~0u)
V = 0;
else if (el->nodeid)
V = convertMDNode(el->nodeid);
else
V = MDString::get(Context, "");
} else if (el->string) {
V = MDString::get(Context, StringRef(el->string, el->len));
} else { |
2c859ec7 |
#if LLVM_VERSION < 36 |
53bd5bb1 |
V = ConstantInt::get(IntegerType::get(Context, el->len),
el->constant); |
2c859ec7 |
#else
V = ConstantAsMetadata::get(ConstantInt::get(IntegerType::get(Context, el->len),
el->constant));
#endif |
53bd5bb1 |
}
Vals[j] = V;
} |
2c859ec7 |
#if LLVM_VERSION < 36 |
59f1b78b |
MDNode *N = MDNode::get(Context, ARRAYREF(Value*,Vals, node->numelements)); |
2c859ec7 |
#else
MDNode *N = MDNode::get(Context, ARRAYREF(Metadata*,Vals, node->numelements));
#endif |
53bd5bb1 |
delete[] Vals;
mdnodes[i] = N;
return N;
}
|
9463f9fd |
void AddStackProtect(Function *F)
{
BasicBlock &BB = F->getEntryBlock();
if (isa<AllocaInst>(BB.begin())) {
// Have an alloca -> some instruction uses its address otherwise
// mem2reg would have converted it to an SSA register.
// Enable stack protector for this function. |
9f8df4ae |
#if LLVM_VERSION < 29 |
0c79cc55 |
// LLVM 2.9 has broken SSP, it does a 'mov 0x28, $rax', which tries
// to read from the address 0x28 and crashes |
9463f9fd |
F->addFnAttr(Attribute::StackProtectReq); |
0c79cc55 |
#endif |
9463f9fd |
} |
f1d071c8 |
// always add stackprotect attribute (bb #2239), so we know this
// function was verified. If there is no alloca it won't actually add
// stack protector in emitted code so this won't slow down the app. |
9f8df4ae |
#if LLVM_VERSION < 29 |
f1d071c8 |
F->addFnAttr(Attribute::StackProtect); |
0c79cc55 |
#endif |
9463f9fd |
}
|
b2de4fd8 |
Value *GEPOperand(Value *V) {
if (LoadInst *LI = dyn_cast<LoadInst>(V)) {
Value *VI = LI->getOperand(0);
StoreInst *SI = 0;
for (Value::use_iterator I=VI->use_begin(),
E=VI->use_end(); I != E; ++I) { |
1300b002 |
Value *I_V = *I;
if (StoreInst *S = dyn_cast<StoreInst>(I_V)) { |
b2de4fd8 |
if (SI)
return V;
SI = S; |
1300b002 |
} else if (!isa<LoadInst>(I_V)) |
b2de4fd8 |
return V;
}
V = SI->getOperand(0);
} |
34d8b8cf |
#if LLVM_VERSION < 32 |
b2de4fd8 |
if (EE->getTargetData()->getPointerSize() == 8) { |
34d8b8cf |
#else
if (EE->getDataLayout()->getPointerSize() == 8) {
#endif |
b2de4fd8 |
// eliminate useless trunc, GEP can take i64 too
if (TruncInst *I = dyn_cast<TruncInst>(V)) {
Value *Src = I->getOperand(0);
if (Src->getType() == Type::getInt64Ty(Context) &&
I->getType() == Type::getInt32Ty(Context))
return Src;
}
}
return V;
}
|
6e52ce67 |
Function* generate() {
PrettyStackTraceString CrashInfo("Generate LLVM IR functions");
apiMap.irgenTimer.startTimer(); |
e32caecb |
TypeMap = new LLVMTypeMapper(Context, bc->types + 4, bc->num_types - 5); |
53bd5bb1 |
for (unsigned i=0;i<bc->dbgnode_cnt;i++) {
mdnodes.push_back(convertMDNode(i));
} |
6b67ec6e |
|
9fac78c7 |
for (unsigned i=0;i<cli_apicall_maxglobal - _FIRST_GLOBAL;i++) { |
80b4b3fb |
unsigned id = cli_globals[i].globalid; |
59f1b78b |
constType *Ty = apiMap.get(cli_globals[i].type, NULL, NULL); |
322a0ea6 |
/*if (const ArrayType *ATy = dyn_cast<ArrayType>(Ty))
Ty = PointerType::getUnqual(ATy->getElementType());*/ |
646395bb |
GVtypeMap[id] = Ty; |
80b4b3fb |
} |
a45e2577 |
|
d0af4afe |
// The hidden ctx param to all functions |
aa745db7 |
unsigned maxh = cli_globals[0].offset + sizeof(struct cli_bc_hooks); |
59f1b78b |
constType *HiddenCtx = PointerType::getUnqual(ArrayType::get(Type::getInt8Ty(Context), maxh)); |
d0af4afe |
|
ec077929 |
globals.reserve(bc->num_globals); |
459b13ed |
BitVector FakeGVs; |
284ffd21 |
FakeGVs.resize(bc->num_globals); |
65c740d7 |
globals.push_back(0); |
284ffd21 |
for (unsigned i=1;i<bc->num_globals;i++) { |
59f1b78b |
constType *Ty = mapType(bc->globaltys[i]); |
1d1dd80f |
|
ec077929 |
// TODO: validate number of components against type_components
unsigned c = 0; |
459b13ed |
GlobalVariable *GV;
if (isa<PointerType>(Ty)) { |
80b4b3fb |
unsigned g = bc->globals[i][1];
if (GVoffsetMap.count(g)) { |
284ffd21 |
FakeGVs.set(i); |
459b13ed |
globals.push_back(0);
continue;
}
} |
1d1dd80f |
Constant *C = buildConstant(Ty, bc->globals[i], c); |
459b13ed |
GV = new GlobalVariable(*M, Ty, true,
GlobalValue::InternalLinkage,
C, "glob"+Twine(i)); |
ec077929 |
globals.push_back(GV);
} |
ee8f1888 |
Function **Functions = new Function*[bc->num_func]; |
daffb518 |
for (unsigned j=0;j<bc->num_func;j++) { |
3b33bd68 |
// Create LLVM IR Function |
daffb518 |
const struct cli_bc_func *func = &bc->funcs[j]; |
59f1b78b |
std::vector<constType*> argTypes; |
d0af4afe |
argTypes.push_back(HiddenCtx); |
daffb518 |
for (unsigned a=0;a<func->numArgs;a++) { |
3b33bd68 |
argTypes.push_back(mapType(func->types[a]));
} |
59f1b78b |
constType *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); |
6e52ce67 |
Functions[j]->setLinkage(GlobalValue::InternalLinkage); |
3eef86ee |
#ifdef C_LINUX
/* bb #2270, this should really be fixed either by LLVM or GCC.*/ |
34d8b8cf |
#if LLVM_VERSION < 32 |
3eef86ee |
Functions[j]->addFnAttr(Attribute::constructStackAlignmentFromInt(16)); |
34d8b8cf |
#else
// TODO: How does this translate?
// Functions[j]->addFnAttr(Attribute::StackAlignment);
#endif |
3eef86ee |
#endif |
ee8f1888 |
} |
59f1b78b |
constType *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]; |
9e1f0d90 |
bool broken = false; |
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(); |
3cd48316 |
assert(F->arg_size() == (unsigned)(func->numArgs + 1) && "Mismatched args"); |
d0af4afe |
++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; |
459b13ed |
if (FakeGVs.any()) {
Argument *Ctx = F->arg_begin();
for (unsigned i=0;i<bc->num_globals;i++) { |
284ffd21 |
if (!FakeGVs[i]) |
459b13ed |
continue; |
80b4b3fb |
unsigned g = bc->globals[i][1];
unsigned offset = GVoffsetMap[g]; |
aa745db7 |
|
80b4b3fb |
Constant *Idx = ConstantInt::get(Type::getInt32Ty(Context),
offset); |
aa745db7 |
Value *Idxs[2] = {
ConstantInt::get(Type::getInt32Ty(Context), 0),
Idx
}; |
59f1b78b |
Value *GEP = Builder.CreateInBoundsGEP(Ctx, ARRAYREF(Value*, Idxs, Idxs+2));
constType *Ty = GVtypeMap[g]; |
a45e2577 |
Ty = PointerType::getUnqual(PointerType::getUnqual(Ty));
Value *Cast = Builder.CreateBitCast(GEP, Ty); |
80b4b3fb |
Value *SpecialGV = Builder.CreateLoad(Cast); |
59f1b78b |
constType *IP8Ty = Type::getInt8Ty(Context); |
236fb136 |
IP8Ty = PointerType::getUnqual(IP8Ty);
SpecialGV = Builder.CreateBitCast(SpecialGV, IP8Ty); |
688799d1 |
SpecialGV->setName("g"+Twine(g-_FIRST_GLOBAL)+"_"); |
a45e2577 |
Value *C[] = {
ConstantInt::get(Type::getInt32Ty(Context), bc->globals[i][0])
}; |
59f1b78b |
globals[i] = createGEP(SpecialGV, 0, ARRAYREF(Value*,C, C+1)); |
322a0ea6 |
if (!globals[i]) { |
29553b8e |
if (cli_debug_flag) {
std::string str;
raw_string_ostream ostr(str);
ostr << i << ":" << g << ":" << bc->globals[i][0] <<"\n";
Ty->print(ostr);
cli_dbgmsg_internal("[Bytecode JIT]: %s\n", ostr.str().c_str());
} |
322a0ea6 |
llvm_report_error("(libclamav) unable to create fake global");
} |
236fb136 |
globals[i] = Builder.CreateBitCast(globals[i], Ty);
if(GetElementPtrInst *GI = dyn_cast<GetElementPtrInst>(globals[i])) { |
322a0ea6 |
GI->setIsInBounds(true); |
688799d1 |
GI->setName("geped"+Twine(i)+"_");
} |
459b13ed |
}
}
|
3b33bd68 |
// Generate LLVM IR for each BB |
9e1f0d90 |
for (unsigned i=0;i<func->numBB && !broken;i++) { |
a45e2577 |
bool unreachable = false; |
3b33bd68 |
const struct cli_bc_bb *bb = &func->BB[i];
Builder.SetInsertPoint(BB[i]); |
53bd5bb1 |
unsigned c = 0; |
9e1f0d90 |
for (unsigned j=0;j<bb->numInsts && !broken;j++) { |
ee8f1888 |
const struct cli_bc_inst *inst = &bb->insts[j]; |
a7b7a648 |
Value *Op0=0, *Op1=0, *Op2=0; |
ee8f1888 |
// libclamav has already validated this. |
52dd3a6b |
assert(inst->opcode < OP_BC_INVALID && "Invalid opcode"); |
53bd5bb1 |
if (func->dbgnodes) {
if (func->dbgnodes[c] != ~0u) {
unsigned j = func->dbgnodes[c];
assert(j < mdnodes.size());
Builder.SetCurrentDebugLocation(mdnodes[j]);
} else
Builder.SetCurrentDebugLocation(0);
}
c++; |
ee8f1888 |
switch (inst->opcode) { |
52dd3a6b |
case OP_BC_JMP:
case OP_BC_BRANCH:
case OP_BC_CALL_API:
case OP_BC_CALL_DIRECT:
case OP_BC_ZEXT:
case OP_BC_SEXT:
case OP_BC_TRUNC:
case OP_BC_GEP1: |
9463f9fd |
case OP_BC_GEPZ: |
52dd3a6b |
case OP_BC_GEPN:
case OP_BC_STORE:
case OP_BC_COPY: |
a45e2577 |
case OP_BC_RET: |
1e30496d |
case OP_BC_PTRDIFF32: |
44e13431 |
case OP_BC_PTRTOINT64: |
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]); |
322a0ea6 |
if (Op0->getType() != Op1->getType()) {
Op0->dump();
Op1->dump();
llvm_report_error("(libclamav) binop type mismatch");
} |
ee8f1888 |
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) { |
52dd3a6b |
case OP_BC_ADD: |
ee8f1888 |
Store(inst->dest, Builder.CreateAdd(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_SUB: |
ee8f1888 |
Store(inst->dest, Builder.CreateSub(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_MUL: |
ee8f1888 |
Store(inst->dest, Builder.CreateMul(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_UDIV: |
6b67ec6e |
{
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0)); |
57bbb2eb |
InsertVerify(Bad, Fail, CF->FHandler, F); |
ee8f1888 |
Store(inst->dest, Builder.CreateUDiv(Op0, Op1));
break; |
6b67ec6e |
} |
52dd3a6b |
case OP_BC_SDIV: |
6b67ec6e |
{
//TODO: also verify Op0 == -1 && Op1 = INT_MIN
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0)); |
57bbb2eb |
InsertVerify(Bad, Fail, CF->FHandler, F); |
ee8f1888 |
Store(inst->dest, Builder.CreateSDiv(Op0, Op1));
break; |
6b67ec6e |
} |
52dd3a6b |
case OP_BC_UREM: |
6b67ec6e |
{
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0)); |
57bbb2eb |
InsertVerify(Bad, Fail, CF->FHandler, F); |
ee8f1888 |
Store(inst->dest, Builder.CreateURem(Op0, Op1));
break; |
6b67ec6e |
} |
52dd3a6b |
case OP_BC_SREM: |
6b67ec6e |
{
//TODO: also verify Op0 == -1 && Op1 = INT_MIN
Value *Bad = Builder.CreateICmpEQ(Op1, ConstantInt::get(Op1->getType(), 0)); |
57bbb2eb |
InsertVerify(Bad, Fail, CF->FHandler, F); |
ee8f1888 |
Store(inst->dest, Builder.CreateSRem(Op0, Op1));
break; |
6b67ec6e |
} |
52dd3a6b |
case OP_BC_SHL: |
ee8f1888 |
Store(inst->dest, Builder.CreateShl(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_LSHR: |
ee8f1888 |
Store(inst->dest, Builder.CreateLShr(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_ASHR: |
ee8f1888 |
Store(inst->dest, Builder.CreateAShr(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_AND: |
ee8f1888 |
Store(inst->dest, Builder.CreateAnd(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_OR: |
ee8f1888 |
Store(inst->dest, Builder.CreateOr(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_XOR: |
ee8f1888 |
Store(inst->dest, Builder.CreateXor(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_TRUNC: |
ee8f1888 |
{
Value *Src = convertOperand(func, inst, inst->u.cast.source); |
59f1b78b |
constType *Ty = mapType(func->types[inst->dest]); |
ee8f1888 |
Store(inst->dest, Builder.CreateTrunc(Src, Ty));
break;
} |
52dd3a6b |
case OP_BC_ZEXT: |
ee8f1888 |
{
Value *Src = convertOperand(func, inst, inst->u.cast.source); |
59f1b78b |
constType *Ty = mapType(func->types[inst->dest]); |
ee8f1888 |
Store(inst->dest, Builder.CreateZExt(Src, Ty));
break;
} |
52dd3a6b |
case OP_BC_SEXT: |
ee8f1888 |
{
Value *Src = convertOperand(func, inst, inst->u.cast.source); |
59f1b78b |
constType *Ty = mapType(func->types[inst->dest]); |
ee8f1888 |
Store(inst->dest, Builder.CreateSExt(Src, Ty));
break;
} |
52dd3a6b |
case OP_BC_BRANCH: |
ee8f1888 |
{
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)) { |
29553b8e |
cli_warnmsg("[Bytecode JIT]: type mismatch in condition"); |
9e1f0d90 |
broken = true;
break; |
ee8f1888 |
}
Builder.CreateCondBr(Cond, True, False);
break;
} |
52dd3a6b |
case OP_BC_JMP: |
ee8f1888 |
{
BasicBlock *Jmp = BB[inst->u.jump];
Builder.CreateBr(Jmp);
break;
} |
52dd3a6b |
case OP_BC_RET: |
a45e2577 |
{
Op0 = convertOperand(func, F->getReturnType(), inst->u.unaryop); |
ee8f1888 |
Builder.CreateRet(Op0);
break; |
a45e2577 |
} |
d38d6dad |
case OP_BC_RET_VOID:
Builder.CreateRetVoid();
break; |
52dd3a6b |
case OP_BC_ICMP_EQ: |
ee8f1888 |
Store(inst->dest, Builder.CreateICmpEQ(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_ICMP_NE: |
ee8f1888 |
Store(inst->dest, Builder.CreateICmpNE(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_ICMP_UGT: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpUGT(Op0, Op1)); |
ee8f1888 |
break; |
52dd3a6b |
case OP_BC_ICMP_UGE: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpUGE(Op0, Op1)); |
3b33bd68 |
break; |
52dd3a6b |
case OP_BC_ICMP_ULT: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpULT(Op0, Op1)); |
ee8f1888 |
break; |
52dd3a6b |
case OP_BC_ICMP_ULE: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpULE(Op0, Op1)); |
ee8f1888 |
break; |
52dd3a6b |
case OP_BC_ICMP_SGT: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpSGT(Op0, Op1)); |
ee8f1888 |
break; |
52dd3a6b |
case OP_BC_ICMP_SGE: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpSGE(Op0, Op1)); |
ee8f1888 |
break; |
52dd3a6b |
case OP_BC_ICMP_SLT: |
6b67ec6e |
Store(inst->dest, Builder.CreateICmpSLT(Op0, Op1)); |
ee8f1888 |
break; |
b331b610 |
case OP_BC_ICMP_SLE:
Store(inst->dest, Builder.CreateICmpSLE(Op0, Op1));
break; |
52dd3a6b |
case OP_BC_SELECT: |
ee8f1888 |
Store(inst->dest, Builder.CreateSelect(Op0, Op1, Op2));
break; |
52dd3a6b |
case OP_BC_COPY: |
a1781898 |
{
Value *Dest = Values[inst->u.binop[1]]; |
59f1b78b |
constPointerType *PTy = cast<PointerType>(Dest->getType()); |
a1781898 |
Op0 = convertOperand(func, PTy->getElementType(), inst->u.binop[0]); |
fa0a9143 |
PTy = PointerType::getUnqual(Op0->getType());
Dest = Builder.CreateBitCast(Dest, PTy); |
a1781898 |
Builder.CreateStore(Op0, Dest); |
ee8f1888 |
break; |
a1781898 |
} |
52dd3a6b |
case OP_BC_CALL_DIRECT: |
ee8f1888 |
{
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 |
} |
59f1b78b |
CallInst *CI = Builder.CreateCall(DestF, ARRAYREF(Value*, args.begin(), args.end())); |
d0af4afe |
CI->setCallingConv(CallingConv::Fast); |
34d8b8cf |
#if LLVM_VERSION < 32 |
b2de4fd8 |
CI->setDoesNotThrow(true); |
34d8b8cf |
#else
CI->setDoesNotThrow();
#endif |
d38d6dad |
if (CI->getType()->getTypeID() != Type::VoidTyID)
Store(inst->dest, CI); |
ee8f1888 |
break;
} |
52dd3a6b |
case OP_BC_CALL_API: |
e32caecb |
{
assert(inst->u.ops.funcid < cli_apicall_maxapi && "APICall out of range");
std::vector<Value*> args;
Function *DestF = apiFuncs[inst->u.ops.funcid]; |
ab402e6a |
if (!strcmp(cli_apicalls[inst->u.ops.funcid].name, "engine_functionality_level")) {
Store(inst->dest,
ConstantInt::get(Type::getInt32Ty(Context),
cl_retflevel()));
} else { |
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 |
} |
59f1b78b |
CallInst *CI = Builder.CreateCall(DestF, ARRAYREFVECTOR(Value*, args)); |
34d8b8cf |
#if LLVM_VERSION < 32 |
b2de4fd8 |
CI->setDoesNotThrow(true); |
34d8b8cf |
#else
CI->setDoesNotThrow();
#endif |
b2de4fd8 |
Store(inst->dest, CI); |
ab402e6a |
} |
e32caecb |
break;
} |
52dd3a6b |
case OP_BC_GEP1: |
e6d1fe78 |
{ |
59f1b78b |
constType *SrcTy = mapType(inst->u.three[0]); |
688799d1 |
Value *V = convertOperand(func, SrcTy, inst->u.three[1]); |
1e30496d |
Value *Op = convertOperand(func, I32Ty, inst->u.three[2]); |
b2de4fd8 |
Op = GEPOperand(Op); |
3aca6c4a |
if (!createGEP(inst->dest, V, ARRAYREF(Value*, &Op, &Op+1))) { |
9e1f0d90 |
cli_warnmsg("[Bytecode JIT]: OP_BC_GEP1 createGEP failed\n");
broken = true; |
3aca6c4a |
} |
e6d1fe78 |
break;
} |
9463f9fd |
case OP_BC_GEPZ:
{
Value *Ops[2];
Ops[0] = ConstantInt::get(Type::getInt32Ty(Context), 0); |
59f1b78b |
constType *SrcTy = mapType(inst->u.three[0]); |
9463f9fd |
Value *V = convertOperand(func, SrcTy, inst->u.three[1]); |
1e30496d |
Ops[1] = convertOperand(func, I32Ty, inst->u.three[2]); |
b2de4fd8 |
Ops[1] = GEPOperand(Ops[1]); |
3aca6c4a |
if (!createGEP(inst->dest, V, ARRAYREF(Value*, Ops, Ops+2))) { |
9e1f0d90 |
cli_warnmsg("[Bytecode JIT]: OP_BC_GEPZ createGEP failed\n");
broken = true; |
3aca6c4a |
} |
9463f9fd |
break;
} |
52dd3a6b |
case OP_BC_GEPN: |
e6d1fe78 |
{
std::vector<Value*> Idxs; |
688799d1 |
assert(inst->u.ops.numOps > 2); |
59f1b78b |
constType *SrcTy = mapType(inst->u.ops.ops[0]); |
688799d1 |
Value *V = convertOperand(func, SrcTy, inst->u.ops.ops[1]); |
236fb136 |
for (unsigned a=2;a<inst->u.ops.numOps;a++) { |
1e30496d |
Value *Op = convertOperand(func, I32Ty, inst->u.ops.ops[a]); |
b2de4fd8 |
Op = GEPOperand(Op); |
236fb136 |
Idxs.push_back(Op);
} |
3aca6c4a |
if (!createGEP(inst->dest, V, ARRAYREFVECTOR(Value*, Idxs))) { |
9e1f0d90 |
cli_warnmsg("[Bytecode JIT]: OP_BC_GEPN createGEP failed\n");
broken = true; |
3aca6c4a |
} |
e6d1fe78 |
break;
} |
52dd3a6b |
case OP_BC_STORE: |
e6d1fe78 |
{
Value *Dest = convertOperand(func, inst, inst->u.binop[1]); |
322a0ea6 |
Value *V = convertOperand(func, inst, inst->u.binop[0]); |
59f1b78b |
constType *VPTy = PointerType::getUnqual(V->getType()); |
322a0ea6 |
if (VPTy != Dest->getType())
Dest = Builder.CreateBitCast(Dest, VPTy);
Builder.CreateStore(V, Dest); |
e6d1fe78 |
break;
} |
52dd3a6b |
case OP_BC_LOAD: |
a45e2577 |
{
Op0 = Builder.CreateBitCast(Op0,
Values[inst->dest]->getType()); |
a1781898 |
Op0 = Builder.CreateLoad(Op0); |
e6d1fe78 |
Store(inst->dest, Op0);
break; |
a45e2577 |
}
case OP_BC_MEMSET:
{
Value *Dst = convertOperand(func, inst, inst->u.three[0]); |
6ea339ae |
Dst = Builder.CreatePointerCast(Dst, PointerType::getUnqual(Type::getInt8Ty(Context))); |
a45e2577 |
Value *Val = convertOperand(func, Type::getInt8Ty(Context), inst->u.three[1]); |
99aeada8 |
Value *Len = convertOperand(func, Type::getInt32Ty(Context), inst->u.three[2]); |
deafd8fa |
#if LLVM_VERSION < 29 |
9f8df4ae |
CallInst *c = Builder.CreateCall4(CF->FMemset, Dst, Val, Len,
ConstantInt::get(Type::getInt32Ty(Context), 1));
#else |
59f1b78b |
CallInst *c = Builder.CreateCall5(CF->FMemset, Dst, Val, Len,
ConstantInt::get(Type::getInt32Ty(Context), 1),
ConstantInt::get(Type::getInt1Ty(Context), 0)
);
#endif |
a45e2577 |
c->setTailCall(true);
c->setDoesNotThrow(); |
744c40d1 |
UpgradeCall(c, CF->FMemset); |
a45e2577 |
break;
}
case OP_BC_MEMCPY:
{
Value *Dst = convertOperand(func, inst, inst->u.three[0]); |
6ea339ae |
Dst = Builder.CreatePointerCast(Dst, PointerType::getUnqual(Type::getInt8Ty(Context))); |
a45e2577 |
Value *Src = convertOperand(func, inst, inst->u.three[1]); |
6ea339ae |
Src = Builder.CreatePointerCast(Src, PointerType::getUnqual(Type::getInt8Ty(Context))); |
99aeada8 |
Value *Len = convertOperand(func, Type::getInt32Ty(Context), inst->u.three[2]); |
deafd8fa |
#if LLVM_VERSION < 29 |
9f8df4ae |
CallInst *c = Builder.CreateCall4(CF->FMemcpy, Dst, Src, Len,
ConstantInt::get(Type::getInt32Ty(Context), 1));
#else |
59f1b78b |
CallInst *c = Builder.CreateCall5(CF->FMemcpy, Dst, Src, Len,
ConstantInt::get(Type::getInt32Ty(Context), 1),
ConstantInt::get(Type::getInt1Ty(Context), 0)
);
#endif |
a45e2577 |
c->setTailCall(true);
c->setDoesNotThrow(); |
744c40d1 |
UpgradeCall(c, CF->FMemcpy); |
a45e2577 |
break;
}
case OP_BC_MEMMOVE:
{
Value *Dst = convertOperand(func, inst, inst->u.three[0]); |
6ea339ae |
Dst = Builder.CreatePointerCast(Dst, PointerType::getUnqual(Type::getInt8Ty(Context))); |
a45e2577 |
Value *Src = convertOperand(func, inst, inst->u.three[1]); |
b0a5ddb3 |
Src = Builder.CreatePointerCast(Src, PointerType::getUnqual(Type::getInt8Ty(Context))); |
99aeada8 |
Value *Len = convertOperand(func, Type::getInt32Ty(Context), inst->u.three[2]); |
deafd8fa |
#if LLVM_VERSION < 29 |
9f8df4ae |
CallInst *c = Builder.CreateCall4(CF->FMemmove, Dst, Src, Len,
ConstantInt::get(Type::getInt32Ty(Context), 1));
#else |
59f1b78b |
CallInst *c = Builder.CreateCall5(CF->FMemmove, Dst, Src, Len,
ConstantInt::get(Type::getInt32Ty(Context), 1),
ConstantInt::get(Type::getInt1Ty(Context), 0));
#endif |
a45e2577 |
c->setTailCall(true);
c->setDoesNotThrow(); |
744c40d1 |
UpgradeCall(c, CF->FMemmove); |
a45e2577 |
break;
}
case OP_BC_MEMCMP:
{
Value *Dst = convertOperand(func, inst, inst->u.three[0]); |
6ea339ae |
Dst = Builder.CreatePointerCast(Dst, PointerType::getUnqual(Type::getInt8Ty(Context))); |
a45e2577 |
Value *Src = convertOperand(func, inst, inst->u.three[1]); |
b0a5ddb3 |
Src = Builder.CreatePointerCast(Src, PointerType::getUnqual(Type::getInt8Ty(Context))); |
34d8b8cf |
#if LLVM_VERSION < 32 |
a45e2577 |
Value *Len = convertOperand(func, EE->getTargetData()->getIntPtrType(Context), inst->u.three[2]); |
34d8b8cf |
#else
Value *Len = convertOperand(func, EE->getDataLayout()->getIntPtrType(Context), inst->u.three[2]);
#endif |
57bbb2eb |
CallInst *c = Builder.CreateCall3(CF->FRealmemcmp, Dst, Src, Len); |
a45e2577 |
c->setTailCall(true);
c->setDoesNotThrow();
Store(inst->dest, c);
break;
}
case OP_BC_ISBIGENDIAN:
Store(inst->dest, WORDS_BIGENDIAN ?
ConstantInt::getTrue(Context) :
ConstantInt::getFalse(Context));
break;
case OP_BC_ABORT:
if (!unreachable) { |
57bbb2eb |
CallInst *CI = Builder.CreateCall(CF->FHandler); |
a45e2577 |
CI->setDoesNotReturn();
CI->setDoesNotThrow();
Builder.CreateUnreachable();
unreachable = true;
}
break; |
8997b147 |
case OP_BC_BSWAP16:
{ |
57bbb2eb |
CallInst *C = Builder.CreateCall(CF->FBSwap16, convertOperand(func, inst, inst->u.unaryop)); |
8997b147 |
C->setTailCall(true); |
34d8b8cf |
#if LLVM_VERSION < 32 |
8997b147 |
C->setDoesNotThrow(true); |
34d8b8cf |
#else
C->setDoesNotThrow();
#endif |
8997b147 |
Store(inst->dest, C);
break;
}
case OP_BC_BSWAP32:
{ |
57bbb2eb |
CallInst *C = Builder.CreateCall(CF->FBSwap32, convertOperand(func, inst, inst->u.unaryop)); |
8997b147 |
C->setTailCall(true); |
34d8b8cf |
#if LLVM_VERSION < 32 |
8997b147 |
C->setDoesNotThrow(true); |
34d8b8cf |
#else
C->setDoesNotThrow();
#endif |
8997b147 |
Store(inst->dest, C);
break;
}
case OP_BC_BSWAP64:
{ |
57bbb2eb |
CallInst *C = Builder.CreateCall(CF->FBSwap64, convertOperand(func, inst, inst->u.unaryop)); |
8997b147 |
C->setTailCall(true); |
34d8b8cf |
#if LLVM_VERSION < 32 |
8997b147 |
C->setDoesNotThrow(true); |
34d8b8cf |
#else
C->setDoesNotThrow();
#endif |
8997b147 |
Store(inst->dest, C);
break;
} |
4395bb9a |
case OP_BC_PTRDIFF32:
{
Value *P1 = convertOperand(func, inst, inst->u.binop[0]);
Value *P2 = convertOperand(func, inst, inst->u.binop[1]);
P1 = Builder.CreatePtrToInt(P1, Type::getInt64Ty(Context));
P2 = Builder.CreatePtrToInt(P2, Type::getInt64Ty(Context));
Value *R = Builder.CreateSub(P1, P2);
R = Builder.CreateTrunc(R, Type::getInt32Ty(Context));
Store(inst->dest, R);
break;
} |
44e13431 |
case OP_BC_PTRTOINT64:
{
Value *P1 = convertOperand(func, inst, inst->u.unaryop);
P1 = Builder.CreatePtrToInt(P1, Type::getInt64Ty(Context));
Store(inst->dest, P1);
break;
} |
ee8f1888 |
default: |
29553b8e |
cli_warnmsg("[Bytecode JIT]: JIT doesn't implement opcode %d yet!\n",
inst->opcode); |
9e1f0d90 |
broken = true;
break; |
3b33bd68 |
}
}
}
|
9e1f0d90 |
// If successful so far, run verifyFunction
if (!broken) { |
4a40b53a |
#if LLVM_VERSION < 35 |
9e1f0d90 |
if (verifyFunction(*F, PrintMessageAction)) { |
4a40b53a |
#else
if (verifyFunction(*F, &errs())) {
#endif |
9e1f0d90 |
// verification failed
broken = true;
cli_warnmsg("[Bytecode JIT]: Verification failed\n");
if (cli_debug_flag) {
std::string str;
raw_string_ostream ostr(str);
F->print(ostr);
cli_dbgmsg_internal("[Bytecode JIT]: %s\n", ostr.str().c_str());
} |
29553b8e |
} |
9e1f0d90 |
}
delete [] Values;
// Cleanup after failure and return 0
if (broken) { |
3aca6c4a |
for (unsigned z=0; z < func->numBB ; z++) {
delete BB[z];
}
delete [] BB;
apiMap.irgenTimer.stopTimer();
delete TypeMap;
for (unsigned z=0; z < bc->num_func; z++) {
delete Functions[z];
}
delete [] Functions; |
6e52ce67 |
return 0; |
ee8f1888 |
} |
9e1f0d90 |
|
6b67ec6e |
delete [] BB; |
6e52ce67 |
apiMap.irgenTimer.stopTimer();
apiMap.pmTimer.startTimer();
if (bc->trusted) {
PM.doInitialization();
PM.run(*F);
PM.doFinalization();
}
else {
PMUnsigned.doInitialization();
PMUnsigned.run(*F);
PMUnsigned.doFinalization();
}
apiMap.pmTimer.stopTimer();
apiMap.irgenTimer.startTimer(); |
ee8f1888 |
} |
556eaf04 |
for (unsigned j=0;j<bc->num_func;j++) {
Function *F = Functions[j];
AddStackProtect(F);
} |
e32caecb |
delete TypeMap; |
59f1b78b |
std::vector<constType*> args; |
a45e2577 |
args.clear(); |
aa745db7 |
args.push_back(HiddenCtx); |
d0af4afe |
FunctionType *Callable = FunctionType::get(Type::getInt32Ty(Context),
args, false); |
6b67ec6e |
|
6e52ce67 |
// If prototype matches, add to callable functions
if (Functions[0]->getFunctionType() != Callable) { |
29553b8e |
cli_warnmsg("[Bytecode JIT]: Wrong prototype for function 0 in bytecode %d\n", bc->id); |
3aca6c4a |
apiMap.irgenTimer.stopTimer();
for (unsigned z=0; z < bc->num_func; z++) {
delete Functions[z];
}
delete [] Functions; |
6e52ce67 |
return 0;
}
// All functions have the Fast calling convention, however
// entrypoint can only be C, emit wrapper
Function *F = Function::Create(Functions[0]->getFunctionType(),
Function::ExternalLinkage, |
34d8b8cf |
#if LLVM_VERSION < 33 |
6e52ce67 |
Functions[0]->getName()+"_wrap", M); |
34d8b8cf |
#else
Functions[0]->getName().str()+"_wrap", M);
#endif |
6e52ce67 |
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);
} |
59f1b78b |
CallInst *CI = CallInst::Create(Functions[0], ARRAYREFVECTOR(Value*, Args), "", BB); |
6e52ce67 |
CI->setCallingConv(CallingConv::Fast);
ReturnInst::Create(Context, CI, BB); |
d0af4afe |
|
6e52ce67 |
delete [] Functions; |
4a40b53a |
#if LLVM_VERSION < 35 |
6e52ce67 |
if (verifyFunction(*F, PrintMessageAction)) |
4a40b53a |
#else
if (verifyFunction(*F, &errs()))
#endif |
6e52ce67 |
return 0;
/* DEBUG(errs() << "Generating code\n"); |
853b6e5d |
// Codegen current function as executable machine code. |
f121d43d |
EE->getPointerToFunction(Functions[j]); |
853b6e5d |
void *code = EE->getPointerToFunction(F); |
6e52ce67 |
DEBUG(errs() << "Code generation finished\n");*/ |
d0af4afe |
|
6e52ce67 |
// compiledFunctions[func] = code;
apiMap.irgenTimer.stopTimer();
return F; |
daffb518 |
} |
3b33bd68 |
}; |
ee6ab4f8 |
static sys::Mutex llvm_api_lock;
// This class automatically acquires the lock when instantiated,
// and releases the lock when leaving scope.
class LLVMApiScopedLock {
public:
// when multithreaded mode is false (no atomics available),
// we need to wrap all LLVM API calls with a giant mutex lock, but
// only then.
LLVMApiScopedLock() { |
a7cf187a |
// It is safer to just run all codegen under the mutex,
// it is not like we are going to codegen from multiple threads
// at a time anyway.
// if (!llvm_is_multithreaded()) |
2c859ec7 |
#if LLVM_VERSION < 36 |
ee6ab4f8 |
llvm_api_lock.acquire(); |
2c859ec7 |
#else
llvm_api_lock.lock();
#endif |
ee6ab4f8 |
}
~LLVMApiScopedLock() { |
a7cf187a |
// if (!llvm_is_multithreaded()) |
2c859ec7 |
#if LLVM_VERSION < 36 |
ee6ab4f8 |
llvm_api_lock.release(); |
2c859ec7 |
#else
llvm_api_lock.unlock();
#endif |
ee6ab4f8 |
}
};
|
57bbb2eb |
static void addFunctionProtos(struct CommonFunctions *CF, ExecutionEngine *EE, Module *M)
{
LLVMContext &Context = M->getContext();
FunctionType *FTy = FunctionType::get(Type::getVoidTy(Context),
false);
CF->FHandler = Function::Create(FTy, Function::ExternalLinkage,
"clamjit.fail", M);
CF->FHandler->setDoesNotReturn();
CF->FHandler->setDoesNotThrow(); |
34d8b8cf |
#if LLVM_VERSION == 32
CF->FHandler->addFnAttr(Attributes::NoInline);
#else |
57bbb2eb |
CF->FHandler->addFnAttr(Attribute::NoInline); |
34d8b8cf |
#endif |
2c859ec7 |
#if LLVM_VERSION < 36
// addGlobalMapping still exists in LLVM 3.6, but it doesn't work with MCJIT, which now replaces the JIT implementation. |
57bbb2eb |
EE->addGlobalMapping(CF->FHandler, (void*)(intptr_t)jit_exception_handler); |
2c859ec7 |
#else
sys::DynamicLibrary::AddSymbol(CF->FHandler->getName(), (void*)(intptr_t)jit_exception_handler);
#endif |
57bbb2eb |
EE->InstallLazyFunctionCreator(noUnknownFunctions); |
f121d43d |
EE->getPointerToFunction(CF->FHandler); |
57bbb2eb |
|
59f1b78b |
std::vector<constType*> args; |
57bbb2eb |
args.push_back(PointerType::getUnqual(Type::getInt8Ty(Context)));
args.push_back(Type::getInt8Ty(Context)); |
99aeada8 |
args.push_back(Type::getInt32Ty(Context)); |
57bbb2eb |
args.push_back(Type::getInt32Ty(Context)); |
deafd8fa |
#if LLVM_VERSION >= 29 |
59f1b78b |
args.push_back(Type::getInt1Ty(Context));
#endif |
57bbb2eb |
FunctionType* FuncTy_3 = FunctionType::get(Type::getVoidTy(Context),
args, false);
CF->FMemset = Function::Create(FuncTy_3, GlobalValue::ExternalLinkage, |
deafd8fa |
#if LLVM_VERSION < 29 |
99aeada8 |
"llvm.memset.i32", |
9f8df4ae |
#else |
99aeada8 |
"llvm.memset.p0i8.i32", |
59f1b78b |
#endif |
fb2b05cb |
M); |
57bbb2eb |
CF->FMemset->setDoesNotThrow(); |
34d8b8cf |
#if LLVM_VERSION < 32 |
57bbb2eb |
CF->FMemset->setDoesNotCapture(1, true); |
34d8b8cf |
#else
CF->FMemset->setDoesNotCapture(1);
#endif |
57bbb2eb |
args.clear();
args.push_back(PointerType::getUnqual(Type::getInt8Ty(Context)));
args.push_back(PointerType::getUnqual(Type::getInt8Ty(Context))); |
99aeada8 |
args.push_back(Type::getInt32Ty(Context)); |
57bbb2eb |
args.push_back(Type::getInt32Ty(Context)); |
deafd8fa |
#if LLVM_VERSION >= 29 |
59f1b78b |
args.push_back(Type::getInt1Ty(Context));
#endif |
57bbb2eb |
FunctionType* FuncTy_4 = FunctionType::get(Type::getVoidTy(Context),
args, false);
CF->FMemmove = Function::Create(FuncTy_4, GlobalValue::ExternalLinkage, |
deafd8fa |
#if LLVM_VERSION < 29 |
99aeada8 |
"llvm.memmove.i32", |
9f8df4ae |
#else |
99aeada8 |
"llvm.memmove.p0i8.i32", |
59f1b78b |
#endif |
fb2b05cb |
M); |
57bbb2eb |
CF->FMemmove->setDoesNotThrow(); |
34d8b8cf |
#if LLVM_VERSION < 32 |
57bbb2eb |
CF->FMemmove->setDoesNotCapture(1, true); |
34d8b8cf |
#else
CF->FMemmove->setDoesNotCapture(1);
#endif |
57bbb2eb |
CF->FMemcpy = Function::Create(FuncTy_4, GlobalValue::ExternalLinkage, |
deafd8fa |
#if LLVM_VERSION < 29 |
99aeada8 |
"llvm.memcpy.i32", |
9f8df4ae |
#else |
99aeada8 |
"llvm.memcpy.p0i8.p0i8.i32", |
59f1b78b |
#endif |
fb2b05cb |
M); |
57bbb2eb |
CF->FMemcpy->setDoesNotThrow(); |
34d8b8cf |
#if LLVM_VERSION < 32 |
57bbb2eb |
CF->FMemcpy->setDoesNotCapture(1, true); |
34d8b8cf |
#else
CF->FMemcpy->setDoesNotCapture(1);
#endif |
57bbb2eb |
args.clear();
args.push_back(Type::getInt16Ty(Context));
FunctionType *FuncTy_5 = FunctionType::get(Type::getInt16Ty(Context), args, false);
CF->FBSwap16 = Function::Create(FuncTy_5, GlobalValue::ExternalLinkage,
"llvm.bswap.i16", M);
CF->FBSwap16->setDoesNotThrow();
args.clear();
args.push_back(Type::getInt32Ty(Context));
FunctionType *FuncTy_6 = FunctionType::get(Type::getInt32Ty(Context), args, false);
CF->FBSwap32 = Function::Create(FuncTy_6, GlobalValue::ExternalLinkage,
"llvm.bswap.i32", M);
CF->FBSwap32->setDoesNotThrow();
args.clear();
args.push_back(Type::getInt64Ty(Context));
FunctionType *FuncTy_7 = FunctionType::get(Type::getInt64Ty(Context), args, false);
CF->FBSwap64 = Function::Create(FuncTy_7, GlobalValue::ExternalLinkage,
"llvm.bswap.i64", M);
CF->FBSwap64->setDoesNotThrow();
FunctionType* DummyTy = FunctionType::get(Type::getVoidTy(Context), false);
CF->FRealmemset = Function::Create(DummyTy, GlobalValue::ExternalLinkage,
"memset", M); |
2c859ec7 |
#if LLVM_VERSION < 36 |
57bbb2eb |
EE->addGlobalMapping(CF->FRealmemset, (void*)(intptr_t)memset); |
2c859ec7 |
#else
sys::DynamicLibrary::AddSymbol(CF->FRealmemset->getName(), (void*)(intptr_t)memset);
#endif |
f121d43d |
EE->getPointerToFunction(CF->FRealmemset); |
57bbb2eb |
CF->FRealMemmove = Function::Create(DummyTy, GlobalValue::ExternalLinkage,
"memmove", M); |
2c859ec7 |
#if LLVM_VERSION < 36 |
57bbb2eb |
EE->addGlobalMapping(CF->FRealMemmove, (void*)(intptr_t)memmove); |
2c859ec7 |
#else
sys::DynamicLibrary::AddSymbol(CF->FRealMemmove->getName(), (void*)(intptr_t)memmove);
#endif |
f121d43d |
EE->getPointerToFunction(CF->FRealMemmove); |
57bbb2eb |
CF->FRealmemcpy = Function::Create(DummyTy, GlobalValue::ExternalLinkage,
"memcpy", M); |
2c859ec7 |
#if LLVM_VERSION < 36 |
57bbb2eb |
EE->addGlobalMapping(CF->FRealmemcpy, (void*)(intptr_t)memcpy); |
2c859ec7 |
#else
sys::DynamicLibrary::AddSymbol(CF->FRealmemcpy->getName(), (void*)(intptr_t)memcpy);
#endif |
f121d43d |
EE->getPointerToFunction(CF->FRealmemcpy); |
57bbb2eb |
args.clear();
args.push_back(PointerType::getUnqual(Type::getInt8Ty(Context)));
args.push_back(PointerType::getUnqual(Type::getInt8Ty(Context))); |
34d8b8cf |
#if LLVM_VERSION < 32 |
57bbb2eb |
args.push_back(EE->getTargetData()->getIntPtrType(Context)); |
34d8b8cf |
#else
args.push_back(EE->getDataLayout()->getIntPtrType(Context));
#endif |
57bbb2eb |
FuncTy_5 = FunctionType::get(Type::getInt32Ty(Context),
args, false);
CF->FRealmemcmp = Function::Create(FuncTy_5, GlobalValue::ExternalLinkage, "memcmp", M); |
2c859ec7 |
#if LLVM_VERSION < 36 |
57bbb2eb |
EE->addGlobalMapping(CF->FRealmemcmp, (void*)(intptr_t)memcmp); |
2c859ec7 |
#else
sys::DynamicLibrary::AddSymbol(CF->FRealmemcmp->getName(), (void*)(intptr_t)memcmp);
#endif |
f121d43d |
EE->getPointerToFunction(CF->FRealmemcmp); |
57bbb2eb |
}
|
d1487222 |
} |
9f8df4ae |
#if LLVM_VERSION >= 29 |
0c79cc55 |
INITIALIZE_PASS_BEGIN(RuntimeLimits, "rl", "Runtime Limits", false, false)
INITIALIZE_PASS_DEPENDENCY(LoopInfo)
INITIALIZE_PASS_DEPENDENCY(ScalarEvolution) |
4a40b53a |
#if LLVM_VERSION < 35 |
0c79cc55 |
INITIALIZE_PASS_DEPENDENCY(DominatorTree) |
4a40b53a |
#else
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
#endif |
0c79cc55 |
INITIALIZE_PASS_END(RuntimeLimits, "rl" ,"Runtime Limits", false, false)
#endif |
d1487222 |
|
e51fc058 |
static pthread_mutex_t watchdog_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t watchdog_cond = PTHREAD_COND_INITIALIZER; |
bb5572cb |
static pthread_cond_t watchdog_cond2 = PTHREAD_COND_INITIALIZER; |
e51fc058 |
static int watchdog_running = 0;
struct watchdog_item { |
bdd9aeae |
volatile uint8_t* timeout; |
e51fc058 |
struct timespec abstimeout;
struct watchdog_item *next; |
bb5572cb |
int in_use; |
884a0b8f |
};
|
e51fc058 |
static struct watchdog_item* watchdog_head = NULL;
static struct watchdog_item* watchdog_tail = NULL;
|
bb5572cb |
extern "C" const char *cli_strerror(int errnum, char* buf, size_t len); |
e51fc058 |
#define WATCHDOG_IDLE 10 |
bdd9aeae |
static void *bytecode_watchdog(void *arg)
{ |
e51fc058 |
struct timeval tv;
struct timespec out; |
bb5572cb |
int ret;
char err[128]; |
e51fc058 |
pthread_mutex_lock(&watchdog_mutex);
if (cli_debug_flag)
cli_dbgmsg_internal("bytecode watchdog is running\n");
do {
struct watchdog_item *item;
gettimeofday(&tv, NULL);
out.tv_sec = tv.tv_sec + WATCHDOG_IDLE;
out.tv_nsec = tv.tv_usec*1000;
/* wait for some work, up to WATCHDOG_IDLE time */ |
bb5572cb |
while (watchdog_head == NULL) {
ret = pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex,
&out);
if (ret == ETIMEDOUT)
break;
if (ret) {
cli_warnmsg("bytecode_watchdog: cond_timedwait(1) failed: %s\n",
cli_strerror(ret, err, sizeof(err)));
break;
}
} |
e51fc058 |
if (watchdog_head == NULL)
break;
/* wait till timeout is reached on this item */
item = watchdog_head; |
bb5572cb |
while (item == watchdog_head) {
item->in_use = 1;
ret = pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex,
&item->abstimeout);
if (ret == ETIMEDOUT)
break;
if (ret) {
cli_warnmsg("bytecode_watchdog: cond_timedwait(2) failed: %s\n",
cli_strerror(ret, err, sizeof(err)));
break;
}
}
item->in_use = 0;
pthread_cond_signal(&watchdog_cond2); |
e51fc058 |
if (item != watchdog_head)
continue;/* got removed meanwhile */
/* timeout reached, signal it to bytecode */
*item->timeout = 1; |
162c2e43 |
cli_warnmsg("[Bytecode JIT]: Bytecode run timed out, timeout flag set\n"); |
e51fc058 |
watchdog_head = item->next;
if (!watchdog_head)
watchdog_tail = NULL;
} while (1);
watchdog_running = 0;
if (cli_debug_flag)
cli_dbgmsg_internal("bytecode watchdog quiting\n");
pthread_mutex_unlock(&watchdog_mutex); |
bdd9aeae |
return NULL;
}
|
e51fc058 |
static void watchdog_disarm(struct watchdog_item *item)
{
struct watchdog_item *q, *p = NULL;
if (!item)
return;
pthread_mutex_lock(&watchdog_mutex);
for (q=watchdog_head;q && q != item;p = q, q = q->next) {}
if (q == item) {
if (p)
p->next = q->next;
if (q == watchdog_head)
watchdog_head = q->next;
if (q == watchdog_tail)
watchdog_tail = p;
} |
bb5572cb |
/* don't remove the item from the list until the watchdog is sleeping on
* item, or it'll wake up on uninit data */
while (item->in_use) {
pthread_cond_signal(&watchdog_cond);
pthread_cond_wait(&watchdog_cond2, &watchdog_mutex);
} |
e51fc058 |
pthread_mutex_unlock(&watchdog_mutex);
}
static int watchdog_arm(struct watchdog_item *item, int ms, volatile uint8_t *timeout)
{
int rc = 0;
struct timeval tv0;
*timeout = 0;
item->timeout = timeout;
item->next = NULL; |
bb5572cb |
item->in_use = 0; |
e51fc058 |
gettimeofday(&tv0, NULL);
tv0.tv_usec += ms * 1000;
item->abstimeout.tv_sec = tv0.tv_sec + tv0.tv_usec/1000000;
item->abstimeout.tv_nsec = (tv0.tv_usec%1000000)*1000;
pthread_mutex_lock(&watchdog_mutex);
if (!watchdog_running) {
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if ((rc = pthread_create(&thread, &attr, bytecode_watchdog, NULL))) {
char buf[256];
cli_errmsg("(watchdog) pthread_create failed: %s\n", cli_strerror(rc, buf, sizeof(buf)));
} |
bb5572cb |
if (!rc)
watchdog_running = 1; |
e51fc058 |
pthread_attr_destroy(&attr);
}
if (!rc) {
if (watchdog_tail)
watchdog_tail->next = item;
watchdog_tail = item;
if (!watchdog_head)
watchdog_head = item;
}
pthread_cond_signal(&watchdog_cond);
pthread_mutex_unlock(&watchdog_mutex);
return rc;
}
|
bdd9aeae |
static int bytecode_execute(intptr_t code, struct cli_bc_ctx *ctx) |
d1487222 |
{ |
162c2e43 |
ScopedExceptionHandler handler; |
85a25497 |
// execute; |
3a35520b |
HANDLER_TRY(handler) { |
6b67ec6e |
// setup exception handler to longjmp back here |
3cd48316 |
uint32_t result = ((uint32_t (*)(struct cli_bc_ctx *))(intptr_t)code)(ctx); |
6b67ec6e |
*(uint32_t*)ctx->values = result;
return 0;
} |
3a35520b |
HANDLER_END(handler); |
162c2e43 |
cli_warnmsg("[Bytecode JIT]: JITed code intercepted runtime error!\n"); |
bdd9aeae |
return CL_EBYTECODE; |
884a0b8f |
}
int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx,
const struct cli_bc_func *func)
{
int ret;
struct timeval tv0, tv1; |
e51fc058 |
struct watchdog_item witem; |
884a0b8f |
// no locks needed here, since LLVM automatically acquires a JIT lock
// if needed.
void *code = bcs->engine->compiledFunctions[func];
if (!code) { |
29553b8e |
cli_warnmsg("[Bytecode JIT]: Unable to find compiled function\n"); |
884a0b8f |
if (func->numArgs) |
29553b8e |
cli_warnmsg("[Bytecode JIT] Function has %d arguments, it must have 0 to be called as entrypoint\n",
func->numArgs); |
884a0b8f |
return CL_EBYTECODE;
} |
e51fc058 |
if (cli_debug_flag)
gettimeofday(&tv0, NULL); |
bdd9aeae |
|
71ca3536 |
if (ctx->bytecode_timeout) {
/* only spawn if timeout is set.
* we don't set timeout for selfcheck (see bb #2235) */ |
e51fc058 |
if (watchdog_arm(&witem, ctx->bytecode_timeout, &ctx->timeout)) |
71ca3536 |
return CL_EBYTECODE; |
884a0b8f |
}
|
bdd9aeae |
ret = bytecode_execute((intptr_t)code, ctx); |
e51fc058 |
if (ctx->bytecode_timeout)
watchdog_disarm(&witem); |
bdd9aeae |
|
884a0b8f |
if (cli_debug_flag) { |
29553b8e |
long diff; |
884a0b8f |
gettimeofday(&tv1, NULL);
tv1.tv_sec -= tv0.tv_sec;
tv1.tv_usec -= tv0.tv_usec; |
29553b8e |
diff = tv1.tv_sec*1000000 + tv1.tv_usec;
cli_dbgmsg_internal("bytecode finished in %ld us\n", diff); |
884a0b8f |
} |
bdd9aeae |
return ctx->timeout ? CL_ETIMEOUT : ret; |
d1487222 |
}
|
9463f9fd |
static unsigned char name_salt[16] = { 16, 38, 97, 12, 8, 4, 72, 196, 217, 144, 33, 124, 18, 11, 17, 253 };
static void setGuard(unsigned char* guardbuf)
{
char salt[48];
memcpy(salt, name_salt, 16);
for(unsigned i = 16; i < 48; i++) |
34d8b8cf |
salt[i] = cli_rndnum(255); |
9463f9fd |
|
dd2ed14d |
cl_hash_data((char*)"md5", salt, 48, guardbuf, NULL); |
9463f9fd |
} |
34d8b8cf |
#if LLVM_VERSION < 32 |
6e52ce67 |
static void addFPasses(FunctionPassManager &FPM, bool trusted, const TargetData *TD) |
4a40b53a |
#elif LLVM_VERSION < 35 |
34d8b8cf |
static void addFPasses(FunctionPassManager &FPM, bool trusted, const DataLayout *TD) |
2c859ec7 |
#elif LLVM_VERSION < 36 |
4a40b53a |
static void addFPasses(FunctionPassManager &FPM, bool trusted, const Module *M) |
2c859ec7 |
#else
static void addFPasses(FunctionPassManager &FPM, bool trusted, Module *M) |
34d8b8cf |
#endif |
6e52ce67 |
{
// Set up the optimizer pipeline. Start with registering info about how
// the target lays out data structures. |
34d8b8cf |
#if LLVM_VERSION < 32 |
6e52ce67 |
FPM.add(new TargetData(*TD)); |
4a40b53a |
#elif LLVM_VERSION < 35 |
34d8b8cf |
FPM.add(new DataLayout(*TD)); |
2c859ec7 |
#elif LLVM_VERSION < 36 |
4a40b53a |
FPM.add(new DataLayoutPass(M)); |
2c859ec7 |
#else
DataLayoutPass *DLP = new DataLayoutPass();
DLP->doInitialization(*M);
FPM.add(DLP); |
34d8b8cf |
#endif |
6e52ce67 |
// Promote allocas to registers.
FPM.add(createPromoteMemoryToRegisterPass());
FPM.add(new BrSimplifier());
FPM.add(createDeadCodeEliminationPass());
}
|
d1487222 |
int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
{ |
2487a4a3 |
if (!bcs->engine)
return CL_EBYTECODE; |
162c2e43 |
ScopedExceptionHandler handler; |
ee6ab4f8 |
LLVMApiScopedLock scopedLock; |
6b67ec6e |
// setup exception handler to longjmp back here |
3a35520b |
HANDLER_TRY(handler) { |
d1487222 |
// LLVM itself never throws exceptions, but operator new may throw bad_alloc
try {
Module *M = new Module("ClamAV jit module", bcs->engine->Context);
{
// Create the JIT.
std::string ErrorMsg; |
2c859ec7 |
#if LLVM_VERSION < 36 |
04d11afe |
EngineBuilder builder(M); |
2c859ec7 |
#else
EngineBuilder builder(std::move(std::unique_ptr<Module>(M)));
#endif |
34d8b8cf |
#if LLVM_VERSION >= 31
TargetOptions Options;
#ifdef CL_DEBUG
//disable this for now, it leaks
Options.JITEmitDebugInfo = false;
// Options.JITEmitDebugInfo = true;
#else
Options.JITEmitDebugInfo = false;
#endif
#if LLVM_VERSION < 34
Options.DwarfExceptionHandling = false;
#else
// TODO: How to do this now?
#endif
builder.setTargetOptions(Options);
#endif
|
d1487222 |
builder.setErrorStr(&ErrorMsg);
builder.setEngineKind(EngineKind::JIT); |
9463f9fd |
builder.setOptLevel(CodeGenOpt::Default); |
d1487222 |
ExecutionEngine *EE = bcs->engine->EE = builder.create();
if (!EE) {
if (!ErrorMsg.empty()) |
29553b8e |
cli_errmsg("[Bytecode JIT]: error creating execution engine: %s\n",
ErrorMsg.c_str()); |
d1487222 |
else |
29553b8e |
cli_errmsg("[Bytecode JIT]: JIT not registered?\n"); |
d1487222 |
return CL_EBYTECODE;
} |
e2752b2c |
bcs->engine->Listener = new NotifyListener();
EE->RegisterJITEventListener(bcs->engine->Listener); |
1f1a2497 |
// EE->RegisterJITEventListener(createOProfileJITEventListener()); |
2487a4a3 |
// Due to LLVM PR4816 only X86 supports non-lazy compilation, disable
// for now. |
53bd5bb1 |
EE->DisableLazyCompilation(); |
2c859ec7 |
#if LLVM_VERSION < 36 |
6b67ec6e |
EE->DisableSymbolSearching(); |
2c859ec7 |
#else
// This must be enabled for AddSymbol to work.
EE->DisableSymbolSearching(false);
#endif |
d1487222 |
|
57bbb2eb |
struct CommonFunctions CF;
addFunctionProtos(&CF, EE, M);
|
6e52ce67 |
FunctionPassManager OurFPM(M), OurFPMUnsigned(M); |
34d8b8cf |
#if LLVM_VERSION < 32 |
b2de4fd8 |
M->setDataLayout(EE->getTargetData()->getStringRepresentation()); |
34d8b8cf |
#else
M->setDataLayout(EE->getDataLayout()->getStringRepresentation());
#endif
#if LLVM_VERSION < 31 |
b2de4fd8 |
M->setTargetTriple(sys::getHostTriple()); |
34d8b8cf |
#else
M->setTargetTriple(sys::getDefaultTargetTriple());
#endif
#if LLVM_VERSION < 32 |
6e52ce67 |
addFPasses(OurFPM, true, EE->getTargetData());
addFPasses(OurFPMUnsigned, false, EE->getTargetData()); |
4a40b53a |
#elif LLVM_VERSION < 35 |
34d8b8cf |
addFPasses(OurFPM, true, EE->getDataLayout());
addFPasses(OurFPMUnsigned, false, EE->getDataLayout()); |
4a40b53a |
#else
addFPasses(OurFPM, true, M);
addFPasses(OurFPMUnsigned, false, M); |
34d8b8cf |
#endif
|
e32caecb |
|
d0af4afe |
//TODO: create a wrapper that calls pthread_getspecific |
aa745db7 |
unsigned maxh = cli_globals[0].offset + sizeof(struct cli_bc_hooks); |
59f1b78b |
constType *HiddenCtx = PointerType::getUnqual(ArrayType::get(Type::getInt8Ty(bcs->engine->Context), maxh)); |
d0af4afe |
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]; |
59f1b78b |
constFunctionType *FTy = cast<FunctionType>(apiMap.get(69+api->type, NULL, NULL)); |
e32caecb |
Function *F = Function::Create(FTy, Function::ExternalLinkage,
api->name, M);
void *dest;
switch (api->kind) {
case 0: |
3cd48316 |
dest = (void*)(intptr_t)cli_apicalls0[api->idx]; |
e32caecb |
break;
case 1: |
3cd48316 |
dest = (void*)(intptr_t)cli_apicalls1[api->idx]; |
e32caecb |
break; |
2d45ef06 |
case 2:
dest = (void*)(intptr_t)cli_apicalls2[api->idx];
break;
case 3:
dest = (void*)(intptr_t)cli_apicalls3[api->idx];
break; |
b56bea54 |
case 4:
dest = (void*)(intptr_t)cli_apicalls4[api->idx];
break; |
75e18b29 |
case 5:
dest = (void*)(intptr_t)cli_apicalls5[api->idx];
break;
case 6:
dest = (void*)(intptr_t)cli_apicalls6[api->idx];
break; |
6ad39a40 |
case 7:
dest = (void*)(intptr_t)cli_apicalls7[api->idx];
break; |
7a7365ef |
case 8:
dest = (void*)(intptr_t)cli_apicalls8[api->idx];
break;
case 9:
dest = (void*)(intptr_t)cli_apicalls9[api->idx];
break; |
3cd48316 |
default:
llvm_unreachable("invalid api type"); |
e32caecb |
} |
7a7365ef |
if (!dest) {
std::string reason((Twine("No mapping for builtin api ")+api->name).str());
llvm_error_handler(0, reason);
} |
2c859ec7 |
#if LLVM_VERSION < 36 |
e32caecb |
EE->addGlobalMapping(F, dest); |
2c859ec7 |
#else
// addGlobalMapping doesn't work with MCJIT, so use symbol searching instead.
sys::DynamicLibrary::AddSymbol(F->getName(), dest);
#endif |
f121d43d |
EE->getPointerToFunction(F); |
e32caecb |
apiFuncs[i] = F;
}
|
9463f9fd |
// stack protector
FunctionType *FTy = FunctionType::get(Type::getVoidTy(M->getContext()),
false);
GlobalVariable *Guard = new GlobalVariable(*M, PointerType::getUnqual(Type::getInt8Ty(M->getContext())), |
6e52ce67 |
true, GlobalValue::ExternalLinkage, 0, "__stack_chk_guard"); |
9463f9fd |
unsigned plus = 0;
if (2*sizeof(void*) <= 16 && cli_rndnum(2)==2) {
plus = sizeof(void*);
} |
2c859ec7 |
#if LLVM_VERSION < 36 |
9463f9fd |
EE->addGlobalMapping(Guard, (void*)(&bcs->engine->guard.b[plus])); |
2c859ec7 |
#else
sys::DynamicLibrary::AddSymbol(Guard->getName(), (void*)(&bcs->engine->guard.b[plus]));
#endif |
9463f9fd |
setGuard(bcs->engine->guard.b);
bcs->engine->guard.b[plus+sizeof(void*)-1] = 0x00;
// printf("%p\n", *(void**)(&bcs->engine->guard.b[plus]));
Function *SFail = Function::Create(FTy, Function::ExternalLinkage,
"__stack_chk_fail", M); |
2c859ec7 |
#if LLVM_VERSION < 36 |
9463f9fd |
EE->addGlobalMapping(SFail, (void*)(intptr_t)jit_ssp_handler); |
2c859ec7 |
#else
sys::DynamicLibrary::AddSymbol(SFail->getName(), (void*)(intptr_t)jit_ssp_handler);
#endif |
f121d43d |
EE->getPointerToFunction(SFail); |
9463f9fd |
|
6e52ce67 |
llvm::Function **Functions = new Function*[bcs->count]; |
daffb518 |
for (unsigned i=0;i<bcs->count;i++) { |
3b33bd68 |
const struct cli_bc *bc = &bcs->all_bcs[i]; |
49eb0108 |
if (bc->state == bc_skip || bc->state == bc_interp) {
Functions[i] = 0; |
4789b8a5 |
continue; |
49eb0108 |
} |
57bbb2eb |
LLVMCodegen Codegen(bc, M, &CF, bcs->engine->compiledFunctions, EE, |
6e52ce67 |
OurFPM, OurFPMUnsigned, apiFuncs, apiMap);
Function *F = Codegen.generate();
if (!F) { |
29553b8e |
cli_errmsg("[Bytecode JIT]: JIT codegen failed\n"); |
3aca6c4a |
delete [] apiFuncs;
for (unsigned z=0; z < i; z++) {
delete Functions[z];
}
delete [] Functions; |
ee8f1888 |
return CL_EBYTECODE;
} |
6e52ce67 |
Functions[i] = F; |
daffb518 |
} |
6e52ce67 |
delete [] apiFuncs;
bool has_untrusted = false; |
daffb518 |
|
85a25497 |
for (unsigned i=0;i<bcs->count;i++) { |
6e52ce67 |
if (!bcs->all_bcs[i].trusted) {
has_untrusted = true;
break;
} |
85a25497 |
} |
6e52ce67 |
PassManager PM; |
34d8b8cf |
#if LLVM_VERSION < 32 |
6e52ce67 |
PM.add(new TargetData(*EE->getTargetData())); |
4a40b53a |
#elif LLVM_VERSION < 35 |
34d8b8cf |
PM.add(new DataLayout(*EE->getDataLayout())); |
2c859ec7 |
#elif LLVM_VERSION < 36 |
4a40b53a |
PM.add(new DataLayoutPass(M)); |
2c859ec7 |
#else
DataLayoutPass *DLP = new DataLayoutPass();
DLP->doInitialization(*M);
PM.add(DLP); |
34d8b8cf |
#endif |
6e52ce67 |
// TODO: only run this on the untrusted bytecodes, not all of them...
if (has_untrusted)
PM.add(createClamBCRTChecks()); |
2c859ec7 |
#if LLVM_VERSION >= 36
// With LLVM 3.6 (MCJIT) this Pass is required to work around
// a crash in LLVM caused by the SCCP Pass:
// Pass 'Sparse Conditional Constant Propagation' is not initialized.
// Verify if there is a pass dependency cycle.
// Required Passes:
//
// Program received signal SIGSEGV, Segmentation fault.
PM.add(createGVNPass());
#endif |
6e52ce67 |
PM.add(createSCCPPass()); |
ab402e6a |
PM.add(createCFGSimplificationPass()); |
6e52ce67 |
PM.add(createGlobalOptimizerPass());
PM.add(createConstantMergePass()); |
0c79cc55 |
RuntimeLimits *RL = new RuntimeLimits();
PM.add(RL); |
c0a306b2 |
TimerWrapper pmTimer2("Transform passes"); |
6e52ce67 |
pmTimer2.startTimer();
PM.run(*M);
pmTimer2.stopTimer();
DEBUG(M->dump());
|
2c859ec7 |
#if LLVM_VERSION >= 36
EE->finalizeObject();
#endif
|
6e52ce67 |
{
PrettyStackTraceString CrashInfo2("Native machine codegen"); |
c0a306b2 |
TimerWrapper codegenTimer("Native codegen"); |
6e52ce67 |
codegenTimer.startTimer();
// compile all functions now, not lazily!
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) {
Function *Fn = &*I; |
ab402e6a |
if (!Fn->isDeclaration()) { |
6e52ce67 |
EE->getPointerToFunction(Fn); |
ab402e6a |
} |
6e52ce67 |
}
codegenTimer.stopTimer(); |
d1487222 |
} |
6e52ce67 |
for (unsigned i=0;i<bcs->count;i++) {
const struct cli_bc_func *func = &bcs->all_bcs[i].funcs[0]; |
49eb0108 |
if (!Functions[i])
continue;// not JITed |
6e52ce67 |
bcs->engine->compiledFunctions[func] = EE->getPointerToFunction(Functions[i]);
bcs->all_bcs[i].state = bc_jit;
}
delete [] Functions; |
d1487222 |
} |
a5a19f45 |
return CL_SUCCESS; |
d1487222 |
} catch (std::bad_alloc &badalloc) { |
29553b8e |
cli_errmsg("[Bytecode JIT]: bad_alloc: %s\n",
badalloc.what()); |
d1487222 |
return CL_EMEM;
} catch (...) { |
22cb38ed |
cli_errmsg("[Bytecode JIT]: Unexpected unknown exception occurred\n"); |
d1487222 |
return CL_EBYTECODE;
} |
3a35520b |
return 0;
} HANDLER_END(handler);
cli_errmsg("[Bytecode JIT] *** FATAL error encountered during bytecode generation\n");
return CL_EBYTECODE; |
d1487222 |
}
int bytecode_init(void)
{ |
df52b258 |
// If already initialized return |
4a40b53a |
#if LLVM_VERSION < 35 |
ee6ab4f8 |
if (llvm_is_multithreaded()) { |
26b86d8d |
cli_warnmsg("bytecode_init: already initialized\n"); |
ee6ab4f8 |
return CL_EARG;
} |
4a40b53a |
#else
if (!LLVMIsMultithreaded()) {
cli_warnmsg("bytecode_init: LLVM is compiled without multithreading support\n");
}
#endif
|
d6699b05 |
// LLVM safety assertion prevention fix
// TODO: do we want to do a full shutdown?
remove_fatal_error_handler(); |
d1487222 |
llvm_install_error_handler(llvm_error_handler); |
52dd3a6b |
#ifdef CL_DEBUG |
d1487222 |
sys::PrintStackTraceOnErrorSignal(); |
34d8b8cf |
#if LLVM_VERSION >= 34
llvm::EnablePrettyStackTrace();
#endif |
d38d6dad |
#else |
34d8b8cf |
#if LLVM_VERSION < 34 |
d38d6dad |
llvm::DisablePrettyStackTrace = true; |
52dd3a6b |
#endif |
34d8b8cf |
#endif |
d1487222 |
atexit(do_shutdown);
|
34d8b8cf |
#if LLVM_VERSION < 31 |
d2171abf |
#ifdef CL_DEBUG |
11cee1b7 |
//disable this for now, it leaks
llvm::JITEmitDebugInfo = false;
// llvm::JITEmitDebugInfo = true; |
482e97db |
#else
llvm::JITEmitDebugInfo = false; |
d2171abf |
#endif |
482e97db |
llvm::DwarfExceptionHandling = false; |
34d8b8cf |
#endif |
4a40b53a |
#if LLVM_VERSION < 33 |
d1487222 |
llvm_start_multithreaded(); |
4a40b53a |
#else
// This is now deprecated/useless: Multi-threading can only be enabled/disabled with the compile time define LLVM_ENABLE_THREADS in LLVM.
#endif |
d1487222 |
// If we have a native target, initialize it to ensure it is linked in and
// usable by the JIT. |
dec93ea8 |
#ifndef AC_APPLE_UNIVERSAL_BUILD |
d1487222 |
InitializeNativeTarget(); |
2c859ec7 |
#if LLVM_VERSION >= 36
InitializeNativeTargetAsmPrinter();
InitializeNativeTargetAsmParser();
#endif |
dec93ea8 |
#else
InitializeAllTargets();
#endif |
ee6ab4f8 |
|
4a40b53a |
#if LLVM_VERSION < 35 |
ee6ab4f8 |
if (!llvm_is_multithreaded()) { |
4a40b53a |
#else
if (!LLVMIsMultithreaded()) {
#endif |
ee6ab4f8 |
//TODO:cli_dbgmsg
DEBUG(errs() << "WARNING: ClamAV JIT built w/o atomic builtins\n"
<< "On x86 for best performance ClamAV should be built for i686, not i386!\n");
} |
d1487222 |
return 0;
}
// Called once when loading a new set of BC files |
6eeadbfe |
int cli_bytecode_init_jit(struct cli_all_bc *bcs, unsigned dconfmask) |
d1487222 |
{ |
ee6ab4f8 |
LLVMApiScopedLock scopedLock; |
c52e9274 |
bcs->engine = new(std::nothrow) cli_bcengine; |
d1487222 |
if (!bcs->engine)
return CL_EMEM; |
52dd3a6b |
bcs->engine->EE = 0; |
84edf09b |
bcs->engine->Listener = 0; |
d1487222 |
return 0;
}
|
a5a19f45 |
int cli_bytecode_done_jit(struct cli_all_bc *bcs, int partial) |
d1487222 |
{ |
ee6ab4f8 |
LLVMApiScopedLock scopedLock; |
2487a4a3 |
if (bcs->engine) { |
e2752b2c |
if (bcs->engine->EE) { |
84edf09b |
if (bcs->engine->Listener)
bcs->engine->EE->UnregisterJITEventListener(bcs->engine->Listener); |
2487a4a3 |
delete bcs->engine->EE; |
a5a19f45 |
bcs->engine->EE = 0; |
e2752b2c |
}
delete bcs->engine->Listener; |
a5a19f45 |
bcs->engine->Listener = 0;
if (!partial) {
delete bcs->engine;
bcs->engine = 0;
} |
2487a4a3 |
} |
d1487222 |
return 0;
} |
3b33bd68 |
void cli_bytecode_debug(int argc, char **argv)
{
cl::ParseCommandLineOptions(argc, argv);
} |
2487a4a3 |
|
3cd48316 |
typedef struct lines { |
65c740d7 |
MemoryBuffer *buffer; |
853b6e5d |
std::vector<const char*> linev; |
3cd48316 |
} linesTy; |
65c740d7 |
static struct lineprinter { |
3cd48316 |
StringMap<linesTy*> files; |
65c740d7 |
} LinePrinter;
void cli_bytecode_debug_printsrc(const struct cli_bc_ctx *ctx)
{ |
0a11015b |
if (!ctx->file || !ctx->directory || !ctx->line) {
errs() << (ctx->directory ? "d":"null") << ":" << (ctx->file ? "f" : "null")<< ":" << ctx->line << "\n"; |
65c740d7 |
return;
}
// acquire a mutex here
sys::Mutex mtx(false);
sys::SmartScopedLock<false> lock(mtx);
std::string path = std::string(ctx->directory) + "/" + std::string(ctx->file); |
3cd48316 |
StringMap<linesTy*>::iterator I = LinePrinter.files.find(path);
linesTy *lines; |
65c740d7 |
if (I == LinePrinter.files.end()) { |
3cd48316 |
lines = new linesTy; |
65c740d7 |
std::string ErrorMessage; |
9f8df4ae |
#if LLVM_VERSION < 29
lines->buffer = MemoryBuffer::getFile(path, &ErrorMessage); |
4a40b53a |
#elif LLVM_VERSION < 35 |
0c79cc55 |
OwningPtr<MemoryBuffer> File;
error_code ec = MemoryBuffer::getFile(path, File);
if (ec) {
ErrorMessage = ec.message();
lines->buffer = 0;
} else
lines->buffer = File.take(); |
4a40b53a |
#else
ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = MemoryBuffer::getFile(path);
if (!FileOrErr) {
// TODO: How to handle ErrorMessage?
lines->buffer = 0;
}
else {
lines->buffer = FileOrErr.get().release();
} |
0c79cc55 |
#endif |
65c740d7 |
if (!lines->buffer) {
errs() << "Unable to open file '" << path << "'\n"; |
bebd86a6 |
delete lines; |
65c740d7 |
return ;
}
LinePrinter.files[path] = lines;
} else {
lines = I->getValue();
} |
853b6e5d |
while (lines->linev.size() <= ctx->line+1) { |
65c740d7 |
const char *p; |
853b6e5d |
if (lines->linev.empty()) { |
65c740d7 |
p = lines->buffer->getBufferStart(); |
853b6e5d |
lines->linev.push_back(p); |
65c740d7 |
} else { |
853b6e5d |
p = lines->linev.back(); |
65c740d7 |
if (p == lines->buffer->getBufferEnd())
break;
p = strchr(p, '\n');
if (!p) {
p = lines->buffer->getBufferEnd(); |
853b6e5d |
lines->linev.push_back(p); |
65c740d7 |
} else |
853b6e5d |
lines->linev.push_back(p+1); |
65c740d7 |
}
} |
853b6e5d |
if (ctx->line >= lines->linev.size()) { |
0a11015b |
errs() << "Line number " << ctx->line << "out of file\n"; |
65c740d7 |
return;
} |
853b6e5d |
assert(ctx->line < lines->linev.size()); |
e4a0f2c9 |
|
9f8df4ae |
#if LLVM_VERSION < 28 |
e4a0f2c9 |
int line = (int)ctx->line ? (int)ctx->line : -1;
int col = (int)ctx->col ? (int)ctx->col : -1; |
e3a54503 |
//TODO: print this ourselves, instead of using SMDiagnostic |
e4a0f2c9 |
SMDiagnostic diag(ctx->file, line, col, |
853b6e5d |
"", std::string(lines->linev[ctx->line-1], lines->linev[ctx->line]-1)); |
65c740d7 |
diag.Print("[trace]", errs()); |
e3a54503 |
#endif |
65c740d7 |
}
|
2487a4a3 |
int have_clamjit=1; |
d0934caf |
void cli_bytecode_printversion()
{
cl::PrintVersionMessage();
} |
e4a0f2c9 |
|
2a7f1cda |
void cli_printcxxver()
{
/* Try to print information about some commonly used compilers */
#ifdef __GNUC__
printf("GNU C++: %s (%u.%u.%u)\n", __VERSION__, __GNUC__, __GNUC_MINOR__,
__GNUC_PATCHLEVEL__);
#endif
#ifdef __INTEL_COMPILER
printf("Intel Compiler C++ %u\n", __INTEL_COMPILER);
#endif
#ifdef _MSC_VER
printf("Microsoft Visual C++ %u\n", _MSC_VER);
#endif
}
|
e4a0f2c9 |
namespace ClamBCModule {
void stop(const char *msg, llvm::Function* F, llvm::Instruction* I)
{ |
29553b8e |
if (F && F->hasName()) { |
34d8b8cf |
#if LLVM_VERSION < 31 |
29553b8e |
cli_warnmsg("[Bytecode JIT] in function %s: %s", F->getNameStr().c_str(), msg); |
34d8b8cf |
#else
cli_warnmsg("[Bytecode JIT] in function %s: %s", F->getName().str().c_str(), msg);
#endif |
29553b8e |
} else {
cli_warnmsg("[Bytecode JIT] %s", msg);
} |
e4a0f2c9 |
}
} |
daad92ac |
|
9f8df4ae |
#if LLVM_VERSION >= 29 |
2c859ec7 |
#if LLVM_VERSION < 36 |
0c79cc55 |
static Value *findDbgGlobalDeclare(GlobalVariable *V) { |
2c859ec7 |
#else
static Metadata *findDbgGlobalDeclare(GlobalVariable *V) {
#endif |
0c79cc55 |
const Module *M = V->getParent();
NamedMDNode *NMD = M->getNamedMetadata("llvm.dbg.gv");
if (!NMD)
return 0;
for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) {
DIDescriptor DIG(cast<MDNode>(NMD->getOperand(i)));
if (!DIG.isGlobalVariable())
continue;
if (DIGlobalVariable(DIG).getGlobal() == V)
return DIG;
}
return 0;
}
/// Find the debug info descriptor corresponding to this function. |
2c859ec7 |
#if LLVM_VERSION < 36 |
0c79cc55 |
static Value *findDbgSubprogramDeclare(Function *V) { |
2c859ec7 |
#else
static Metadata *findDbgSubprogramDeclare(Function *V) {
#endif |
0c79cc55 |
const Module *M = V->getParent();
NamedMDNode *NMD = M->getNamedMetadata("llvm.dbg.sp");
if (!NMD)
return 0;
for (unsigned i = 0, e = NMD->getNumOperands(); i != e; ++i) {
DIDescriptor DIG(cast<MDNode>(NMD->getOperand(i)));
if (!DIG.isSubprogram())
continue;
if (DISubprogram(DIG).getFunction() == V)
return DIG;
}
return 0;
}
/// Finds the llvm.dbg.declare intrinsic corresponding to this value if any.
/// It looks through pointer casts too.
static const DbgDeclareInst *findDbgDeclare(const Value *V) {
V = V->stripPointerCasts();
if (!isa<Instruction>(V) && !isa<Argument>(V))
return 0;
const Function *F = NULL;
if (const Instruction *I = dyn_cast<Instruction>(V))
F = I->getParent()->getParent();
else if (const Argument *A = dyn_cast<Argument>(V))
F = A->getParent();
for (Function::const_iterator FI = F->begin(), FE = F->end(); FI != FE; ++FI)
for (BasicBlock::const_iterator BI = (*FI).begin(), BE = (*FI).end();
BI != BE; ++BI)
if (const DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(BI))
if (DDI->getAddress() == V)
return DDI;
return 0;
}
static bool getLocationInfo(const Value *V, std::string &DisplayName,
std::string &Type, unsigned &LineNo,
std::string &File, std::string &Dir) { |
34d8b8cf |
#if LLVM_VERSION < 33 |
0c79cc55 |
DICompileUnit Unit; |
34d8b8cf |
#else
StringRef G;
StringRef H;
#endif |
4a40b53a |
#if LLVM_VERSION < 35 |
0c79cc55 |
DIType TypeD; |
4a40b53a |
#endif
StringRef T; |
0c79cc55 |
if (GlobalVariable *GV = dyn_cast<GlobalVariable>(const_cast<Value*>(V))) { |
2c859ec7 |
#if LLVM_VERSION < 36 |
0c79cc55 |
Value *DIGV = findDbgGlobalDeclare(GV); |
2c859ec7 |
#else
Metadata *DIGV = findDbgGlobalDeclare(GV);
#endif |
0c79cc55 |
if (!DIGV) return false;
DIGlobalVariable Var(cast<MDNode>(DIGV));
StringRef D = Var.getDisplayName();
if (!D.empty())
DisplayName = D;
LineNo = Var.getLineNumber(); |
34d8b8cf |
#if LLVM_VERSION < 33 |
0c79cc55 |
Unit = Var.getCompileUnit(); |
34d8b8cf |
#else
G = Var.getFilename();
H = Var.getDirectory();
#endif |
4a40b53a |
#if LLVM_VERSION < 35 |
0c79cc55 |
TypeD = Var.getType(); |
4a40b53a |
#else
T = Var.getType().getName();
#endif |
0c79cc55 |
} else if (Function *F = dyn_cast<Function>(const_cast<Value*>(V))){ |
2c859ec7 |
#if LLVM_VERSION < 36 |
0c79cc55 |
Value *DIF = findDbgSubprogramDeclare(F); |
2c859ec7 |
#else
Metadata *DIF = findDbgSubprogramDeclare(F);
#endif |
0c79cc55 |
if (!DIF) return false;
DISubprogram Var(cast<MDNode>(DIF));
StringRef D = Var.getDisplayName();
if (!D.empty())
DisplayName = D;
LineNo = Var.getLineNumber(); |
34d8b8cf |
#if LLVM_VERSION < 33 |
0c79cc55 |
Unit = Var.getCompileUnit(); |
34d8b8cf |
#else
G = Var.getFilename();
H = Var.getDirectory();
#endif |
4a40b53a |
#if LLVM_VERSION < 35 |
0c79cc55 |
TypeD = Var.getType(); |
4a40b53a |
#else
T = Var.getType().getName();
#endif |
0c79cc55 |
} else {
const DbgDeclareInst *DDI = findDbgDeclare(V);
if (!DDI) return false;
DIVariable Var(cast<MDNode>(DDI->getVariable()));
StringRef D = Var.getName();
if (!D.empty())
DisplayName = D;
LineNo = Var.getLineNumber(); |
34d8b8cf |
#if LLVM_VERSION < 33 |
0c79cc55 |
Unit = Var.getCompileUnit(); |
34d8b8cf |
#else
// getFilename and getDirectory are not defined
G = StringRef();
H = StringRef();
#endif |
4a40b53a |
#if LLVM_VERSION < 35 |
0c79cc55 |
TypeD = Var.getType(); |
4a40b53a |
#else
T = Var.getType().getName();
#endif |
0c79cc55 |
}
|
4a40b53a |
#if LLVM_VERSION < 35
T = TypeD.getName();
#endif |
0c79cc55 |
if (!T.empty())
Type = T; |
34d8b8cf |
#if LLVM_VERSION < 33
StringRef G = Unit.getFilename();
StringRef H = Unit.getDirectory();
#endif
if (!G.empty())
File = G;
if (!H.empty())
Dir = H; |
0c79cc55 |
return true;
}
#endif
|
daad92ac |
void printValue(llvm::Value *V, bool a, bool b) {
std::string DisplayName;
std::string Type;
unsigned Line;
std::string File;
std::string Dir;
if (!getLocationInfo(V, DisplayName, Type, Line, File, Dir)) {
errs() << *V << "\n";
return;
}
errs() << "'" << DisplayName << "' (" << File << ":" << Line << ")";
}
void printLocation(llvm::Instruction *I, bool a, bool b) {
if (MDNode *N = I->getMetadata("dbg")) {
DILocation Loc(N);
errs() << Loc.getFilename() << ":" << Loc.getLineNumber();
if (unsigned Col = Loc.getColumnNumber()) {
errs() << ":" << Col;
}
errs() << ": ";
return;
}
errs() << *I << ":\n";
} |