【问题标题】:Can you dynamically compile and link/load C code into a C program?您可以动态编译和链接/加载 C 代码到 C 程序中吗?
【发布时间】:2019-11-11 17:36:37
【问题描述】:

我读到了eval in C,如果你编写一个 C 字符串解析器/求值器,你可以将它映射到你的主 C 程序中的特定函数。但据我所知,它实际上并没有将它放入可执行内存中,就像JIT compiler 似乎所做的那样。我不完全了解 JIT 编译器(我从未做过),但我明白了要点。

所以我想知道的是,您是否可以在 C 中创建某种 JIT 编译器,而无需做太多的解析 C 字符串和转换为 AST 之类的工作。基本上,你能不能像在 JavaScript 中那样动态地创建一个函数(在 C 中),使得该函数与任何其他 C 函数完全相同(即它直接编译成可执行的机器代码程序的可执行部分之类的东西)。

如果无法做到这一点,第二种方法是动态加载 C 导入/文件/模块。因此,您分离出一个告诉 clang 编译器编译一些库函数的进程,完成后,在不停止当前程序的情况下,它将新程序库链接/附加到自身,因此可以执行代码 那样

如果这是不可能的,也许一个选项是简单地在后台重新编译程序,然后将当前程序与从头启动的新程序交换。不过那只是非常原始。

试图弄清楚您在 C 中是否有一些用于您自己的自定义函数数据类型的结构,然后如何以最优化的方式在 C 中执行该函数。

【问题讨论】:

  • 您链接到的问题中的current top answer 似乎显示了一种编译C 代码并将结果链接到当前程序的方法。这正是您在第三段中所要求的。当然,在典型的通用系统中,完全可以在主内存而不是磁盘或其他存储中编译代码并执行该代码,但这当然比调用现有的编译器和链接器要多得多的工作。需要详细了解目标系统上的 ABI。...
  • ... 在实践中,调用编译器和链接器本质上是不值得的,除非作为一个学术练习,而且在内存中做这一切更不用说。当然,“如果您在 C 中为您自己的自定义函数数据类型提供了一些结构”,那么以这种方式执行函数将不是“最优化的方式”。
  • dlopen (Unix) 或 LoadLibrary (Windows) 是否适合您的需求?他们确实将编译后的二进制文件加载到可执行内存中。
  • 我曾经写过一个成熟的 C 解释器来完成所有这些工作。这是一个有趣的挑战——困难,但可行。今天既困难又容易,因为事情变得更加复杂,尽管现在有一些库可以完成一些我必须手动完成的事情。
  • (续) (1) 如果您想加载并动态链接到 .so 文件中的代码,这就是 dlopen 的用途。 (2) 如果您想跳转到已加载的任意代码(传递任意参数),这就是 libffi 的用途。 (3) 如果您想加载并动态链接到单个 .o 文件(或旧式静态 .a 库),我不知道这样做的好方法,我很想尘埃落定关闭我的旧自制动态链接器,并教它现代目标文件格式。

标签: c jit


【解决方案1】:

在 POSIX 系统(Linix、Mac、UNIX)上,您可以使用 dlopendlsym 函数。这些函数可用于在运行时加载共享库并从中执行函数。

就创建库而言,最简单的做法是将相关源代码写入文件,在单独的进程中运行 gcc 进行编译,然后使用dlopen/dlsym 运行函数它包含。

例如:

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

const char *libsrc =
"#include <stdio.h>\n"
"\n"
"void f1()\n"
"{\n"
"  printf(\"in f1\\n\");\n"
"}\n"
"\n"
"int add(int a, int b)\n"
"{\n"
"  return a+b;\n"
"}\n";

int main()
{
    FILE *libfile = fopen("mylib.c", "w");
    fputs(libsrc, libfile);
    fclose(libfile);
    system("gcc -fPIC -shared -g -Wall -Wextra -o libmylib.so mylib.c");

    void *lib = dlopen("libmylib.so", RTLD_NOW);
    if (!lib) {
        printf("dlopen failed: %s\n", dlerror());
        return 1;
    }

    void (*f)() = dlsym(lib, "f1");
    if (f) {
        f();
    } else {
        printf("dlsym for f1 failed: %s\n", dlerror());
    }

    int (*a)(int, int) = dlsym(lib, "add");
    if (a) {
        int x = a(2,3);
        printf("x=%d\n", x);
    } else {
        printf("dlsym for add failed: %s\n", dlerror());
    }

    dlclose(lib);
    return 0;
}

【讨论】:

    【解决方案2】:

    还有可以用作库的 Tiny C 编译器,您可以使用它即时编译程序并从现有代码中调用新编译的代码中的函数而无需求助于 动态库加载。

    代码不会是最优化的 C,但也不算太差。

    An example in this answer.

    【讨论】:

      【解决方案3】:

      除了 dlload 路由之外,一些表达式求值器和 SIMD 数学内核工具会动态生成代码到内存块中,该内存块可以通过 mprotect(2) 和 PROT_EXEC 执行。

      虽然这通常不是像 C 那样的 HLL,而只是简单的数学表达式。如果您的要求相当简单,那可能是一条路线。通常,它用于对速度敏感的简单功能,因为它们在例如2D/3D 绘图或图像转换

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-01-16
        • 2013-10-04
        • 1970-01-01
        • 2019-01-27
        • 2015-10-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多