失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > ffi 库使用demo

ffi 库使用demo

时间:2020-02-23 18:49:59

相关推荐

ffi 库使用demo

FFI(Foreign Function Interface)允许以一种语言编写的代码调用另一种语言的代码,而libffi库提供了最底层的、与架构相关的、完整的FFI。libffi的作用就相当于编译器,它为多种调用规则提供了一系列高级语言编程接口,然后通过相应接口完成函数调用,底层会根据对应的规则,完成数据准备,生成相应的汇编指令代码。

动态调用C函数,使用libffi提供接口动态调用流程如下:

1. 准备好参数数据及其对应ffi_type数组、返回值内存指针、函数指针

2. 创建与函数特征相匹配的函数原型:ffi_cif对象

3. 使用“ffi_call”来完成函数调用

补全/JaimeCool/article/details/76332930中的源码,编译gcc rtl.cpp -fpermissive -w -lffi

结论:

1、指针functionPtr仅仅定义了一个普通数据类型的指针,并没有保证和函数testFunc拥有相同的函数指针类型,这样就可以通用的执行多种类似的函数

可见使用ffi,只要有函数原型cif对象,函数实现指针,返回值内存指针和函数参数数组,我们就可以实现在运行时动态调用任意C函数

#include <stdio.h>#include <ffi.h>#include <stdlib.h>int testFunc(int m, int n) {printf("params: %d %d \n", m, n);return m+n;}void testCall (void) {testFunc(1, 2);//拿函数指针// int (*funcPointer)(int, int) = &testFunc;void* functionPtr = &testFunc;int argCount = 2;//参数类型数组ffi_type **ffiArgTypes = alloca(sizeof(ffi_type *) *argCount);ffiArgTypes[0] = &ffi_type_sint;ffiArgTypes[1] = &ffi_type_sint;//参数数据数组void **ffiArgs = alloca(sizeof(void *) *argCount);void *ffiArgPtr = alloca(ffiArgTypes[0]->size);int *argPtr = ffiArgPtr;*argPtr = 5;ffiArgs[0] = ffiArgPtr;void *ffiArgPtr2 = alloca(ffiArgTypes[1]->size);int *argPtr2 = ffiArgPtr2;*argPtr2 = 3;ffiArgs[1] = ffiArgPtr2;//生成函数原型 ffi_cfi 对象ffi_cif cif;ffi_type *returnFfiType = &ffi_type_sint;ffi_status ffiPrepStatus = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, (unsigned int)argCount, returnFfiType, ffiArgTypes);if (ffiPrepStatus == FFI_OK) {//生成用于保存返回值的内存void *returnPtr = NULL;if (returnFfiType->size) {returnPtr = alloca(returnFfiType->size);}//根据cif函数原型,函数指针,返回值内存指针,函数参数数据调用这个函数ffi_call(&cif, functionPtr, returnPtr, ffiArgs);//拿到返回值int returnValue = *(int *)returnPtr;printf("ret: %d \n", returnValue);}}int main (){testCall();return 0;}

进阶版本,参考llvm中omp的__tgt_rtl_run_target_region处理方法,限制条件设备函数无返回返回值,处理结果通过参数返回

#include <stdio.h>#include <ffi.h>#include <stdlib.h>#include <stdint.h>#include <stddef.h>#include <cassert>#include <vector>#define OFFLOAD_SUCCESS (0)#define OFFLOAD_FAIL (~0)#define OFFLOAD_DEVICE_DEFAULT-1#define DPxMOD "0x%0*" PRIxPTR#define DPxPTR(ptr) ((int)(2 * sizeof(uintptr_t))), ((uintptr_t)(ptr))// #define DPxPTR(ptr) ptr#define PRIxPTR "l" "x"#define DEBUG_PREFIX "TARGET aarch64 RTL"#define DEBUGP(prefix, ...) \{\fprintf(stderr, "%s --> ", prefix);\fprintf(stderr, __VA_ARGS__);\}/// Emit a message for debugging#define DP(...) \do { \DEBUGP(DEBUG_PREFIX, __VA_ARGS__); \} while (false)#ifdef __cplusplusextern "C" {#endifint32_t __tgt_rtl_run_target_region(int32_t device_id, void *tgt_entry_ptr,void **tgt_args, ptrdiff_t *tgt_offsets,int32_t arg_num);int32_t __tgt_rtl_run_target_team_region(int32_t device_id, void *tgt_entry_ptr,void **tgt_args,ptrdiff_t *tgt_offsets,int32_t arg_num, int32_t team_num,int32_t thread_limit,uint64_t loop_tripcount /*not used*/) {// ignore team num and thread limit.// Use libffi to launch execution.ffi_cif cif;// All args are references.std::vector<ffi_type *> args_types(arg_num, &ffi_type_pointer);std::vector<void *> args(arg_num);std::vector<void *> ptrs(arg_num);// for (int i=0; i<arg_num; i++) printf("params[%d]: %d + %d\n", i, *(int**)tgt_args[i], tgt_offsets[i]);for (int32_t i = 0; i < arg_num; ++i) {ptrs[i] = (void *)((intptr_t)tgt_args[i] + tgt_offsets[i]);// printf("params[%d]: %d\n", i, *(int*)ptrs[i]);args[i] = &ptrs[i];// printf("params[%d]: %d\n", i, *(int*)args[i]);}// 函数输入参数个数及数据类型由arg_num及args_types指定,函数返回值类型为voidffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, arg_num,&ffi_type_void, &args_types[0]);assert(status == FFI_OK && "Unable to prepare target launch!");if (status != FFI_OK)return OFFLOAD_FAIL;DP("Running entry point at " DPxMOD "...\n", DPxPTR(tgt_entry_ptr));void (*entry)(void);*((void**) &entry) = tgt_entry_ptr;// Set NULL for return value Pointer, so return value must from arguement ?ffi_call(&cif, entry, NULL, &args[0]);return OFFLOAD_SUCCESS;}// return RTL->run_region(RTLDeviceID, TgtEntryPtr, TgtVarsPtr, TgtOffsetsint32_t __tgt_rtl_run_target_region(int32_t device_id, void *tgt_entry_ptr,void **tgt_args, ptrdiff_t *tgt_offsets,int32_t arg_num) {// for (int i=0; i<arg_num; i++) printf("params[%d]: %d \n", i, *(int**)tgt_args[i]);// use one team and one thread.return __tgt_rtl_run_target_team_region(device_id, tgt_entry_ptr, tgt_args,tgt_offsets, arg_num, 1, 1, 0);}// omp 函数的传参类似引用,非值传递void testFunc(int &m, int &n) {printf("params: %d %d \n", m, n);m += n;return;}int main (void) {// 获取函数指针void* functionPtr = &testFunc;int argCount = 2;int i;// 参数类型数组, 两个参数的类型为sintffi_type **ffiArgTypes = alloca(sizeof(ffi_type *) *argCount);ffiArgTypes[0] = &ffi_type_sint;ffiArgTypes[1] = &ffi_type_sint;//参数数据数组void **ffiArgs = alloca(sizeof(void *) *argCount);ptrdiff_t *tgt_offsets = alloca(sizeof(ptrdiff_t) *argCount);void *ffiArgPtr = alloca(ffiArgTypes[0]->size);int *argPtr = ffiArgPtr;*argPtr = 5;ffiArgs[0] = ffiArgPtr;void *ffiArgPtr2 = alloca(ffiArgTypes[1]->size);int *argPtr2 = ffiArgPtr2;*argPtr2 = 3;ffiArgs[1] = ffiArgPtr2;for (i=0; i<argCount; i++)tgt_offsets[i] = 0; // 参考processDataBefore,只有OMP_TGT_MAPTYPE_PRIVATE需要设置__tgt_rtl_run_target_region(-1, functionPtr, ffiArgs, tgt_offsets, argCount);printf("return value from param: %d\n", *(int*)ffiArgs[0]);return 0;}#ifdef __cplusplus}#endif

如果觉得《ffi 库使用demo》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。