JIT编译的LLVM IR在FreeBSD下无法调用主程序C函数(MacOS可正常运行)的问题求助
FreeBSD下LLVM JIT无法访问主程序本地函数的解决办法
你的判断完全正确——这个问题确实和FreeBSD的链接器/运行时加载器行为有关,和MacOS的默认行为差异导致了兼容性问题。我来帮你拆解原因并给出具体的解决步骤:
问题根源
FreeBSD的系统链接器ld和运行时加载器ld.so默认不会把主程序(可执行文件)的全局符号导出到动态符号表中,而LLVM JIT生成的代码本质上是在运行时动态加载的“临时共享库”,它只能从动态符号表中查找外部符号。
而MacOS的dyld(动态加载器)默认允许动态加载的代码直接查找主程序的符号,所以你的代码在Mac上能正常运行,但到FreeBSD就会因为找不到test_fun的符号而崩溃。
问题复现代码
示例C++代码
// 省略头文件 extern "C" { int test_fun(int arg) { printf("Super test fun %d\n", arg); return arg + 1; } } int main(int ac, char **av) { InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); InitializeNativeTargetAsmParser(); llvm::LLVMContext ctx; llvm::SMDiagnostic diag; auto M = parseIRFile( av[1], diag, ctx ); printf("Module %llx\n", (uint64_t)M.get()); std::string err; llvm::EngineBuilder EB(std::move(M)); EB.setEngineKind(llvm::EngineKind::JIT).setErrorStr(&err); llvm::ExecutionEngine* EE = EB.create(); if (!EE) return 0; EE->finalizeObject(); const auto fa = (int(*)())EE->getFunctionAddress(av[2]); printf("IR fun '%s' returned %d\n", av[2], fa()); return 0; }
示例IR代码
declare i32 @test_fun(i32) define i32 @test_ll() { %v1 = mul i32 4, 5 %v2 = add i32 3, %v1 %v3 = call i32(i32) @test_fun(i32 %v2) ret i32 %v3 }
解决方案
有两种可靠的方式解决这个问题,你可以根据需求选择:
1. 编译主程序时导出所有全局符号(推荐)
在编译主程序时添加链接选项-Wl,-E(或者兼容GCC的-export-dynamic),这个选项会告诉链接器把所有全局符号都导出到动态符号表,让JIT代码可以访问。
比如你的编译命令可以改成:
clang++ -o jit_test jit_test.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core native` -Wl,-E
2. 显式标记单个函数为可导出
如果你不想导出所有符号,只需要让特定函数被JIT访问,可以给函数添加visibility("default")属性,强制它被导出到动态符号表:
修改你的C++代码中的test_fun:
extern "C" { __attribute__((visibility("default"))) int test_fun(int arg) { printf("Super test fun %d\n", arg); return arg + 1; } }
这种方式不需要修改编译选项,但需要给每个要被JIT调用的函数都添加属性,适合符号数量较少的场景。
验证效果
用上述任意一种方式修改后,重新编译主程序,再运行测试:
./jit_test test.ll test_ll
此时JIT生成的代码应该能正常找到并调用主程序中的test_fun,程序不再崩溃。
内容的提问来源于stack exchange,提问作者mrx
相关产品推荐
相关产品推荐

