LLVM ORC JIT中ReexportsGenerator无法解析符号问题排查
LLVM ORC跨JIT实例符号解析问题排查
问题场景
存在两个LLJIT实例(JIT1、JIT2),尝试让JIT2引用JIT1中的函数时,出现JIT session error: Symbols not found错误。已尝试用ReexportsGenerator将JIT1的主JITDylib导出到JIT2,但未生效。
进一步排查发现:
- 直接调用
JIT1->lookup("my_symbol_name")能正确获取符号地址,确认JIT1中存在目标符号; - 调用
lookupFlags尝试匹配符号时,返回的匹配结果数量为0。
相关代码片段:
定义JITDylib
llvm::orc::JITDylib &JD = JIT2->getMainJITDylib(); llvm::orc::JITDylib &SourceJD = JIT1->getMainJITDylib();
尝试ReexportsGenerator
auto gen = std::make_unique<ReexportsGenerator>(JIT1->getMainJITDylib(), llvm::orc::JITDylibLookupFlags::MatchAllSymbols); JIT2->getMainJITDylib().addGenerator(std::move(gen));
验证lookup成功
llvm::StringRef symName("my_symbol_name"); // 直接lookup能拿到正确地址 auto addr = JIT1->lookup(symName); if (auto E = addr.takeError()) { throw E; } uint64_t fun_addr = addr->getValue(); // 地址正确,确认JIT1持有符号
lookupFlags匹配失败
// 自行创建SymbolStringPool并intern符号名 llvm::orc::SymbolStringPool pool; llvm::orc::SymbolStringPtr symNamePtr = pool.intern(symName); llvm::orc::SymbolLookupSet LookupSet; LookupSet.add(symNamePtr, llvm::orc::SymbolLookupFlags::WeaklyReferencedSymbol); auto Flags = JD.getExecutionSession().lookupFlags( llvm::orc::LookupKind::DLSym, {{&SourceJD, llvm::orc::JITDylibLookupFlags::MatchAllSymbols}}, LookupSet); if (auto E = Flags.takeError()) { throw E; } std::cout << "Flags.size() " << (*Flags).size() << std::endl; // 输出0,匹配失败
核心问题分析
1. SymbolStringPool不共享导致匹配失败
LLVM ORC的SymbolStringPtr依赖同一个SymbolStringPool保证符号唯一性——不同pool中intern相同字符串得到的SymbolStringPtr是不同实例,lookupFlags会严格比对SymbolStringPtr指针而非字符串内容。
你在测试中自行创建了新的SymbolStringPool,但JIT1和JIT2各自持有独立pool,导致你intern出的symNamePtr和JIT1中存储的符号指针不匹配,因此lookupFlags无法找到目标符号。而JIT->lookup(StringRef)会自动将字符串intern到JIT自身的pool,所以能正确匹配。
2. ReexportsGenerator未生效的可能原因
- 符号名不匹配:LLVM默认会对C++函数进行名称修饰(mangling),你使用的
my_symbol_name可能是未修饰的原名,而JIT1中实际存储的是修饰后的符号(比如__Z12my_symbol_namev)。 - JITDylib搜索顺序问题:JIT2的主JITDylib可能优先搜索自身的符号生成器,ReexportsGenerator优先级不足导致未被触发。
- ExecutionSession隔离:两个LLJIT实例默认使用独立的
ExecutionSession,跨会话的符号导出需要额外配置。
解决建议
1. 修复lookupFlags的测试代码
使用JIT1的SymbolStringPool来intern符号名,确保指针一致:
// 改用JIT1的SymbolStringPool llvm::orc::SymbolStringPtr symNamePtr = JIT1->getExecutionSession().getSymbolStringPool().intern(symName); llvm::orc::SymbolLookupSet LookupSet; LookupSet.add(symNamePtr, llvm::orc::SymbolLookupFlags::WeaklyReferencedSymbol); // 或者直接调用JIT1的lookupFlags方法,避免手动指定JITDylib auto Flags = JIT1->lookupFlags(LookupSet);
2. 确保ReexportsGenerator正确工作
- 验证符号的真实名称:使用
llvm::demangle工具查看JIT1中符号的实际名称,确保你使用的符号名和JIT中存储的一致:#include "llvm/Demangle/Demangle.h" // ... std::string demangled = llvm::demangle("__Z12my_symbol_namev"); // 传入修饰后的符号名 - 共享ExecutionSession:如果两个JIT实例需要跨会话导出符号,创建LLJIT时指定同一个
ExecutionSession:auto ES = std::make_unique<llvm::orc::ExecutionSession>(); auto JIT1 = llvm::orc::LLJITBuilder().setExecutionSession(std::move(ES)).create(); auto JIT2 = llvm::orc::LLJITBuilder().setExecutionSession(JIT1->getExecutionSession()).create(); - 调整符号生成器优先级:添加ReexportsGenerator时设置更高的优先级:
JD.addGenerator(std::move(gen), llvm::orc::GeneratorPosition::Front);
3. 替代方案:手动添加符号映射
如果ReexportsGenerator仍不生效,可以直接将JIT1中的符号地址添加到JIT2的JITDylib中:
auto symAddr = JIT1->lookup("my_symbol_name").get(); JD.define(llvm::orc::absoluteSymbols({{JIT2->getExecutionSession().getSymbolStringPool().intern("my_symbol_name"), symAddr}}));
内容的提问来源于stack exchange,提问作者Dmitry Kurtaev
相关产品推荐
相关产品推荐

