skyeye动态翻译之callout

本文介绍了SkyEye仿真器在动态翻译过程中如何调用外部C函数的方法。重点分析了通过LLVM调用C函数的具体实现过程,包括函数指针的设定与使用,以及在不同场景下的调用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面的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函数,进行分发到真正想要的函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值