FFI(Foreign Function Interface)允许以一种语言编写的代码调用另一种语言的代码,而libffi库提供了最底层的、与架构相关的、完整的FFI。libffi的作用就相当于编译器,它为多种调用规则提供了一系列高级语言编程接口,然后通过相应接口完成函数调用,底层会根据对应的规则,完成数据准备,生成相应的汇编指令代码。
动态调用C函数,使用libffi提供接口动态调用流程如下:
1. 准备好参数数据及其对应ffi_type数组、返回值内存指针、函数指针
2. 创建与函数特征相匹配的函数原型:ffi_cif对象
3. 使用“ffi_call”来完成函数调用
补全https://blog.csdn.net/JaimeCool/article/details/中的源码,编译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 __cplusplus extern "C" { #endif int32_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指定,函数返回值类型为void ffi_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, TgtOffsets int32_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; // 参数类型数组, 两个参数的类型为sint ffi_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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/55866.html