LLVM JIT 符号未找到

5

我正在尝试编写一个程序来JIT一些代码。 JIT的代码需要调用正在运行的应用程序进行运行时支持,但是当函数实例化时找不到运行时支持符号。

我尝试遵循Kaleidoscope教程。我需要从一些生成的IR代码中调用运行时中的函数。例如,我想从一些llvm IR中调用此函数。

extern "C" void* llvmNewVector() {
    return new vector<int>();
}

根据万花筒教程,应该在应用程序运行时将其声明为extern "C"。我已经创建了一个函数原型并正确地生成了IR(在检查我正在jit的函数后没有错误)。
对我来说似乎还有更多工作要做,以将此函数链接到jit代码,但是Kaleidoscope教程似乎并没有这样做。
我的问题是,由于外部符号未解析,jit代码无法实现。以下代码会打印“made it here”,但不会继续执行。
cerr << "made it here." << endl;
auto Sym = ExitOnErr(TheJIT->lookup(name));
NativeCodePtr FP = (NativeCodePtr)Sym.getAddress();
assert(FP && "Failed to find function ");
cerr << "returning jitted function " << name << endl;
return FP;

我相信我在做某些事情时出了问题或者漏掉了某些步骤,但我一直没有找到。
我得到的输出是:
made it here.
JIT session error: Symbols not found: { llvmNewVector }
Failed to materialize symbols: { my_test }

使用以下标志编译了代码:LLVM-9

clang++ -I. -g -I../include/ -std=c++11 -fexceptions -fvisibility=hidden -fno-rtti -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.cpp 

以下是所使用的链接:

llvm-config --libs

你的C函数是否在单独的文件中?编译时你使用什么命令? - JKRT
我已经将C函数放在一个单独的文件中进行链接,也将它们移动到现有的源文件中(更像Kaleidoscope示例),但两者都没有起作用。两次都出现了找不到符号的问题。 - Kent Lee
请附上您用于构建源代码的命令,以及您使用的LLVM版本。 - JKRT
我正在尝试使用最新的LLVM(LLVM 9)。编译命令是clang++ -I. -g -I../include/ -std=c++11 -fexceptions -fvisibility=hidden -fno-rtti -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.cpp,链接命令将使用运行llvm-config --libs命令获取的所有库进行链接,并添加ncurses、pthread和z库。 - Kent Lee
我已经更新了你的帖子,加入了这些信息。目前,我有一些事情要处理,等我有时间的时候会回来 : )。在此期间,如果你发现了问题的原因,请随时在此回答,这样可以帮助其他人! - JKRT
4个回答

4
我遇到了同样的问题,并通过以下方式解决:
在旨在解析宿主进程中的符号的教程中,以下代码似乎无法正常工作。请参考如下内容:
ES.getMainJITDylib().setGenerator(
    cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL)));

因此,我手动注册了我想要链接的符号,就像这样:

SymbolMap M;
// Register every symbol that can be accessed from the JIT'ed code.
M[Mangle("llvmNewVector")] = JITEvaluatedSymbol(
    pointerToJITTargetAddress(&llvmNewVector), JITSymbolFlags());
}
cantFail(ES.getMainJITDylib().define(absoluteSymbols(M)));

我在教程中提到的那两行代码后面添加了这段代码。

2

可以考虑为clang添加-Xlinker --export-dynamic选项如何?

我在教程中遇到了类似的问题。 在我的环境(Ubuntu 20.04)中,sincos可以解析,但Kaleidoscope处理器源代码中定义的printdputchard函数无法解析。

编译后,您能否在程序的动态符号表中看到函数名称?

objdump -T program | grep llvmNewVector

如果在objdump中没有-T选项(例如,Mac),那么可能不是这种情况。在我的情况下,printdputchard没有出现在动态符号表中(但出现在符号表中)。
要将这些函数名称添加到动态符号表中,您需要为clang传递-Xlinker --export-dynamic选项(实际上,该选项会传递给ld)。例如(这是教程中的一个示例),
clang++ -Xlinker --export-dynamic -g toy.cpp `llvm-config --ldflags --system-libs --libs all` -O3 -o toy

编译后,函数名称将出现在动态符号表中,教程的示例也能正常工作。


1

这取决于您使用的LLVM版本。LLVM 10有LLJIT类,我是按照以下方式使用它的。

      auto J = ExitOnErr(LLJITBuilder().create());
      auto M = createDemoModule();
    
      auto &dl = J->getDataLayout();
      MangleAndInterner Mangle(J->getExecutionSession(), dl);
      auto &jd = J->getMainJITDylib();
    
      auto s = absoluteSymbols({{ Mangle("printd"), JITEvaluatedSymbol(pointerToJITTargetAddress(&printd), JITSymbolFlags::Exported)}});
      jd.define(s);

printd函数在同一文件中被定义

extern "C" int32_t printd() {
    std::cout << "calling " << __FUNCTION__ << "...\n";
    return 11;
}

0

对于使用LLVM-16的任何人,这是一个解决方案:

int main() {
    ...

    TheJIT = ExitOnError(llvm::orc::KaleidoscopeJIT::Create());

    auto &jd = TheJIT->getMainJITDylib();
    auto mangle = llvm::orc::MangleAndInterner(jd.getExecutionSession(), TheJIT->getDataLayout());

    auto s = [](llvm::orc::MangleAndInterner interner) {
        llvm::orc::SymbolMap symbolMap;
        symbolMap[interner("putchard")] = {
            llvm::pointerToJITTargetAddress(&putchard),
            llvm::JITSymbolFlags(),
        };
        symbolMap[interner("printd")] = {
            llvm::pointerToJITTargetAddress(&printd),
            llvm::JITSymbolFlags(),
        };
        return llvm::orc::absoluteSymbols(symbolMap);
    }(mangle);

    ExitOnError(jd.define(s));

    
    ...
    MainLoop();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接