问题描述
我正在尝试做以下操作
enum types {None, Bool, Short, Char, Integer, Double, Long, Ptr}; int main(int argc, char ** args) { enum types params[10] = {0}; void* triangle = dlopen("./foo.so", RTLD_LAZY); void * fun = dlsym(triangle, ars[1]); <<pseudo code>> }
pseudo代码类似于
fun = {} for param in params: if param == None: fun += void if param == Bool: fun += Boolean if param == Integer: fun += int ... returnVal = fun.pop() funSignature = returnval + " " + funName + "(" + Riffle(fun, ",") + ")" exec funSignature
谢谢
推荐答案
实际上,您几乎可以做所有想要的事情.在C语言中(例如,与C ++不同),共享对象中的功能仅以其名称引用.因此,要找到 - 最重要的是, call - 适当的功能,您不需要它的完整签名.您只需要它的名字!这既是优势又是劣势 - 但这是您选择的语言的本质.
让我演示,它是如何工作的.
#include <dlfcn.h> typedef void* (*arbitrary)(); // do not mix this with typedef void* (*arbitrary)(void); !!! int main() { arbitrary my_function; // Introduce already loaded functions to runtime linker's space void* handle = dlopen(0,RTLD_NOW|RTLD_GLOBAL); // Load the function to our pointer, which doesn't know how many arguments there sould be *(void**)(&my_function) = dlsym(handle,"something"); // Call something via my_function (void) my_function("I accept a string and an integer!\n",(int)(2*2)); return 0; }
实际上,您可以以这种方式调用任何功能.但是,有一个缺点.实际上,您需要在编译时间中知道函数的返回类型.默认情况下,如果您省略了that typedef中的void*,则将int假定为返回类型 - 是的,这是正确的C代码.问题是编译器需要了解返回类型的大小才能正确操作堆栈.
您可以通过技巧进行解决方法,例如,预先删除几种功能类型,具有不同尺寸的返回类型,然后选择您实际要调用的一个功能.但是,更容易的解决方案是需要插件中的功能才能返回void*或始终返回int;实际结果是通过给出的指针作为参数返回的.
您必须确保的是,您始终将其应接受的确切数字和类型调用.密切关注不同整数类型之间的差异(您最好的选择是向他们明确施放参数).
几位评论者报告说,上面的代码不能保证适用于variadic函数(例如printf).
其他推荐答案
什么dlsym()返回通常是函数指针 - 伪装成void *. (如果您要求它提供全局变量的名称,它也会向您返回指向该全局变量的指针.)
您然后像使用其他任何指针来函数一样调用该功能:
int (*fun)(int, char *) = (int (*)(int, char *))dlsym(triangle, "function"); (*fun)(1, "abc"); # Old school - pre-C89 standard, but explicit fun(1, "abc"); # New school - C89/C99 standard, but implicit
我是老学校;我更喜欢明确的符号,以便读者知道"有趣"是指向功能的指针,而无需看到其声明.使用新的学校符号,您必须记住在尝试找到称为'fun()'的函数之前寻找变量'fun'.
请注意,您不能像这样做一样动态构建函数调用 - 或一般而言.要这样做需要更多的工作.您必须提前知道函数指针在参数的方式及其返回的方式以及如何解释所有内容的方式中所期望的.
管理更多动态函数调用(例如PERL)具有有关如何调用函数和参数传递的特殊规则,并且没有使用任意签名来调用(可以说不能调用)函数.他们只能使用预先知道的签名来调用功能.一种机制(PERL未使用)是将参数推到堆栈上,然后调用知道如何从堆栈中收集值的函数.但是,即使称为函数可以操纵这些值,然后调用任意其他函数,该函数也为任意其他函数提供了正确的调用序列.
C中的反射很难 - 非常困难.它不是不可能的 - 但是它需要基础架构来支持它并使用它来使用它,并且只能调用支持基础架构规则的功能.
其他推荐答案
正确的解决方案
假设您正在编写共享库;我发现这个问题的最佳解决方案是严格定义和控制哪些功能是由以下方式动态链接的:
- 设置所有符号隐藏
- 例如,clang -dynamiclib Person.c -fvisibility=hidden -o libPerson.dylib用clang编译
- 然后使用__attribute__((visibility("default")))和extern "C"选择性地UNDIND并包括功能
- 利润!您知道该功能的签名是什么.你写了!
I found this in 苹果的动态库设计指南.这些文档还包括上述问题的其他解决方案.
您问题的答案
如先前的答案所述,C和C ++在> extern "C" 在其定义中没有被弄脏,因此该函数的符号根本不包括完整功能签名.如果您使用C ++无需extern "C"编译,但是函数被填充了,以便您可以将它们删除以获得完整函数的签名(使用 demangler.com 或 >). here there 有关更多详细信息有关更多详细信息什么是乱码.
一般而言,如果您尝试用dlopen导入功能,最好使用第一个选项.