前面的translate framework中我们重点分析了llvm跟skyeye仿真数据的交互,下面分析下动态翻译过程中怎么实现调用外部c函数。这里先介绍下context。在skyeye仿真中不仅包括指令集的模拟,同时作为一个全系统仿真器,skyeye同时也对mmu,io等进行了,这些代码都是c语言实现的,显然在全系统下动态翻译需要使用外部的mmu,io等函数,这里其实也存在两种基本的策略,一种是直接外调c函数;另外就是将这部分功能也翻译成中间码。通常我们选择第一种方法,从代码继承的角度来看,mmu这些代码早已存在并且经过之前的检测和修复,基本上可以认为是正确的。直接来过来会节省时间。如果单从翻译的角度来看第二种方法也是没有必要的:第一,mmu等外设从根本上来说并不是指令执行的,因此不存在需要翻译的问题,至于使用什么语言实现应该没有影响。第二,退一步来说,即便是使用了翻译,最终还是编译成本地码,跟直接写c语言效果相同,但是显然需要翻译时间和相应的工作量。因此使用llvm的翻译仅限于指令翻译,对于其他的mm,tlb,io等外设,直接使用c或者c++实现更加方便。于是存在一个llvm条用c函数的过程,不过在llvm中这个非常容易(trivial).在前面的分析中我们也碰到了一个check_mm的例子,我们以这个例子来分析。
Type const *intptr_type = cpu->dyncom_engine->exec_engine->getTargetData()->getIntPtrType(_CTX());
Constant *v_cpu = ConstantInt::get(intptr_type, (uintptr_t)cpu);
Value *v_cpu_ptr = ConstantExpr::getIntToPtr(v_cpu, PointerType::getUnqual(intptr_type));
std::vector<Value *> params;
params.push_back(v_cpu_ptr);
params.push_back(addr);
params.push_back(CONST(count));
params.push_back(CONST(read));
// XXX synchronize cpu context!
Value *exit_val = CallInst::Create(cpu->dyncom_engine->ptr_func_check_mm, params.begin(), params.end(), "", bb);
这里主要工作就是组装参数,然后调用cpu->dyncom_engine->ptr_func_check_mm即可。这个check_mm的函数指针是怎么来的呢?在cpu_create_function创建jitmain函数就该函数如参进行了约定:
if(!is_user_mode(cpu)){
cpu->dyncom_engine->ptr_func_read_memory = args++;
cpu->dyncom_engine->ptr_func_read_memory->setName("readmemory");
cpu->dyncom_engine->ptr_func_write_memory = args++;
cpu->dyncom_engine->ptr_func_write_memory->setName("writememory");
cpu->dyncom_engine->ptr_func_check_mm = args++;
cpu->dyncom_engine->ptr_func_check_mm->setName("checkmm");
}
在使用该函数时通过函数调用直接将cpu的check_mm参数传了进来:
ret = pfunc(cpu->dyncom_engine->RAM, cpu->rf.grf, cpu->rf.srf, cpu->rf.frf, cpu->mem_ops.read_memory, cpu->mem_ops.write_memory, cpu->mem_ops.check_mm);
另外一种另行是init的时候就安装好的:
static void arch_arm_undef_init(cpu_t *cpu){
//types
std::vector<const Type*> type_func_undef_args;
PointerType *type_intptr = PointerType::get(cpu->dyncom_engine->exec_engine->getTargetData()->getIntPtrType(_CTX()), 0);
const IntegerType *type_i32 = IntegerType::get(_CTX(), 32);
type_func_undef_args.push_back(type_intptr); /* intptr *cpu */
type_func_undef_args.push_back(type_i32); /* unsinged int */
FunctionType *type_func_undef_callout = FunctionType::get(
Type::getInt32Ty(cpu->dyncom_engine->mod->getContext()), //return
type_func_undef_args, /* Params */
false); /* isVarArg */
/* For a custom callout, the dyncom_calloutX functions should be used */
Constant *undef_const = cpu->dyncom_engine->mod->getOrInsertFunction("dyncom_callout", //function name
type_func_undef_callout); //return
if(undef_const == NULL)
fprintf(stderr, "Error:cannot insert function:undefined_instr_callout.\n");
Function *undef_func = cast<Function>(undef_const);
undef_func->setCallingConv(CallingConv::C);
cpu->dyncom_engine->ptr_arch_func[ARM_DYNCOM_CALLOUT_UNDEF] = undef_func;
cpu->dyncom_engine->arch_func[ARM_DYNCOM_CALLOUT_UNDEF] = (void*)arm_undef_instr;
}
首先申明函数原型,然后获取函数dyncom_callout,这个函数就是外面的c函数,这个函数进行了简单的分发。
void arch_arm_undef(cpu_t *cpu, BasicBlock *bb, uint32_t instr)
{
if (cpu->dyncom_engine->ptr_arch_func[ARM_DYNCOM_CALLOUT_UNDEF] == NULL) {
printf("in %s Could not find callout\n", __FUNCTION__);
return;
}
Type const *intptr_type = cpu->dyncom_engine->exec_engine->getTargetData()->getIntPtrType(_CTX());
Constant *v_cpu = ConstantInt::get(intptr_type, (uintptr_t)cpu);
Value *v_cpu_ptr = ConstantExpr::getIntToPtr(v_cpu, PointerType::getUnqual(intptr_type));
std::vector<Value *> params;
params.push_back(v_cpu_ptr);
/* When using a custom callout, must put the callout index as argument for dyncom_callout */
params.push_back(CONST(ARM_DYNCOM_CALLOUT_UNDEF));
//params.push_back(CONST(instr)); // no need for now, the callout func takes no argument
CallInst *ret = CallInst::Create(cpu->dyncom_engine->ptr_arch_func[ARM_DYNCOM_CALLOUT_UNDEF], params.begin(), params.end(), "", bb);
}
这里是函数的调用部分,主要是传入参数。然后调用cpu->dyncom_engine->ptr_arch_func[ARM_DYNCOM_CALLOUT_UNDEF],这个指针就是上面的dyncom_callout,这样就完成了c函数调用。
extern "C" void dyncom_callout(cpu_t *cpu, uint32_t index)
{
if(index > MAX_ARCH_FUNC_NUM || index < 0){
skyeye_log(Error_log, __func__, "In %s Callout function %d not exsit.\n", __func__, index);
skyeye_exit(0);
}
((callout)cpu->dyncom_engine->arch_func[index])(cpu);
}
这个是c函数,进行分发到真正想要的函数。