在编译时使用函数指针而不知道原型

时间:2017-11-12 13:45:15

标签: c linux dlopen

this site开始,我学到了一个令人兴奋的事实,即您可以从共享库加载和运行代码,而无需使用它明确地编译您的程序。

以下代码片段说明了如何使用dlopen()(及相关函数)从C&C的数学库(libm)运行double cos(double)函数:

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>

int main(int argc, char **argv) {
    void *handle;
    double (*cosine)(double);
    char *error;

    handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);
    if (!handle) {
        fputs (dlerror(), stderr);
        exit(1);
    }

    cosine = dlsym(handle, "cos");
    if ((error = dlerror()) != NULL)  {
        fputs(error, stderr);
        exit(1);
    }

    printf ("%f\n", (*cosine)(2.0));
    dlclose(handle);
}

作为一种爱好,我一直在为自定义语言编写一个解释器(在C中),并且我希望用户能够选择共享库并从中运行代码。我知道这是可能的,因为许多语言(包括用C语言编写的Python和J)都可以使用dlopen()动态调用函数,只能在运行时知道将传递给函数并从函数返回的内容。

但是,我无法提前预测我将如何转换从dlsym()返回的void指针。在上面的代码中,程序员知道他们要查找的符号(&#34; cos&#34;)是一个函数,使用一个double作为参数并返回一个double,因此他们知道如何投射cosine变量为&#34;指向函数的指针,该函数接受double并返回double&#34;。

如果我想编写一个用户指定文件名和函数原型的程序,我怎样才能编写调用dlsym()并正确调用函数的底层C代码?

我的理论是,您需要直接从流程堆栈中推送参数和弹出返回值(我不知道如何在没有内联汇编的情况下执行此操作,而我是这样做的。我希望避免),但由于您可能需要通过寄存器传递参数,这会变得复杂。有人知道如何处理这个吗?

1 个答案:

答案 0 :(得分:3)

有许多解决这个问题的知名库,被许多解释语言使用:libffi

在内部,这个库当然使用汇编代码,但它有很多硬件和软件后端,你可以认为它是便携式解决方案