LLVM 8和更高版本的ORC JIT数学库问题

时间:2019-05-01 16:16:26

标签: llvm jit llvm-ir

由于LLVM 8(包括当前的LLVM干线,又名LLVM 9),在将ORC JIT ExecutionEngine与包含对标准数学库的调用的函数一起使用时,我遇到了问题。

JIT编译器能够找到该函数的符号,但如果该函数调用数学库,则无法获取其地址。

我附上一个显示问题的简单漏洞利用程序。程序test.cc读取一个IR文件,该文件在LLVM的中间表示中包含单个功能: 该函数接受一个参数,一个浮点数,并在出现

的情况下返回
  • “ func_works.ll”参数本身,以及
  • “ func_cos_fails.ll”参数的余弦。

我在运行时没有实现两个文件之间的选择,因此切换到另一种情况下需要重新构建程序。

程序使用LLVM附带的标准KaleidoscopeJIT.h(除了我必须公开Datalayout之外)。

如果使用“ func_works.ll”构建程序并运行该程序,则该程序将成功执行:

symbol found!
address found!

如果使用“ func_cos_fails.ll”构建程序并运行该程序,则该程序将失败,并显示以下信息:

symbol found!
Failure value returned from cantFail wrapped call
UNREACHABLE executed at install/llvm-8.0-x86-debug/include/llvm/Support/Error.h:732!

在LLVM 8版本和当前LLVM干线中会发生这种情况。

有人看到发生了什么事吗?

此测试是在配置了LLVM的x86 Linux Ubuntu系统上运行的

cmake -G "Unix Makefiles" \
      -DBUILD_SHARED_LIBS="ON" \
      -DLLVM_ENABLE_RTTI="ON" \
      -DLLVM_ENABLE_ZLIB="OFF" \
      -DLLVM_ENABLE_TERMINFO="OFF" \
      -DCMAKE_BUILD_TYPE="Debug" \
      -DCMAKE_INSTALL_PREFIX=$CMAKE_INSTALL_PREFIX \
      -DLLVM_TARGETS_TO_BUILD="X86" \
      $SRC

test.cc:

#include "KaleidoscopeJIT.h"

#include "llvm/Analysis/Passes.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/ObjectCache.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Support/InitLLVM.h"

#include <iostream>

using namespace llvm;
using namespace llvm::orc;




int main(int argc, char **argv) {
  InitLLVM X(argc, argv);
  EnableDebugBuffering = true;
  LLVMContext Context;

  InitializeNativeTarget();
  InitializeNativeTargetAsmPrinter();
  InitializeNativeTargetAsmParser();

  cl::ParseCommandLineOptions(argc, argv, "Kaleidoscope example program\n");
  SMDiagnostic Err;

  std::unique_ptr<llvm::Module> M = parseIRFile( "func_cos_fails.ll" , Err, Context, false);
  //std::unique_ptr<llvm::Module> M = parseIRFile( "func_works.ll" , Err, Context, false);
  if (!M) {
    Err.print("IR parsing failed: ", errs());
    return 0;
  }

  std::unique_ptr<KaleidoscopeJIT> TheJIT;

  TheJIT = llvm::make_unique<KaleidoscopeJIT>();

  auto H = TheJIT->addModule(std::move(M));

  std::string MangledName;
  llvm::raw_string_ostream MangledNameStream(MangledName);
  llvm::Mangler::getNameWithPrefix(MangledNameStream, "func_ir" , TheJIT->getDL() );

  if (auto Sym = TheJIT->findSymbol(MangledNameStream.str()))
    {
      std::cout << "symbol found!\n";
      void* fptr = (void *)cantFail(Sym.getAddress());

      std::cout << "address found!\n";
    }
  else
    {
      std::cout << "symbol not found!\n";
    }

  return 0;
}

func_cos_fails.ll:

source_filename = "module"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

declare float @cosf(float)

define float @func_ir(float %arg0) {
entrypoint:
  %0 = call float @cosf(float %arg0)
  ret float %0
}

func_works.ll:

source_filename = "module"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"


define float @func_ir(float %arg0) {
entrypoint:
  ret float %arg0
}

KaleidoscopeJIT.h:

#ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H
#define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H

#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Mangler.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
#include <algorithm>
#include <map>
#include <memory>
#include <string>
#include <vector>

namespace llvm {
namespace orc {

class KaleidoscopeJIT {
public:
  using ObjLayerT = LegacyRTDyldObjectLinkingLayer;
  using CompileLayerT = LegacyIRCompileLayer<ObjLayerT, SimpleCompiler>;

  KaleidoscopeJIT()
      : Resolver(createLegacyLookupResolver(
            ES,
            [this](const std::string &Name) {
              return ObjectLayer.findSymbol(Name, true);
            },
            [](Error Err) { cantFail(std::move(Err), "lookupFlags failed"); })),
        TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()),
        ObjectLayer(ES,
                    [this](VModuleKey) {
                      return ObjLayerT::Resources{
                          std::make_shared<SectionMemoryManager>(), Resolver};
                    }),
        CompileLayer(ObjectLayer, SimpleCompiler(*TM)) {
    llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);
  }

  TargetMachine &getTargetMachine() { return *TM; }

  VModuleKey addModule(std::unique_ptr<Module> M) {
    auto K = ES.allocateVModule();
    cantFail(CompileLayer.addModule(K, std::move(M)));
    ModuleKeys.push_back(K);
    return K;
  }

  void removeModule(VModuleKey K) {
    ModuleKeys.erase(find(ModuleKeys, K));
    cantFail(CompileLayer.removeModule(K));
  }

  JITSymbol findSymbol(const std::string Name) {
    return findMangledSymbol(mangle(Name));
  }

  const DataLayout& getDL() const {
    return DL;
  }

private:
  std::string mangle(const std::string &Name) {
    std::string MangledName;
    {
      raw_string_ostream MangledNameStream(MangledName);
      Mangler::getNameWithPrefix(MangledNameStream, Name, DL);
    }
    return MangledName;
  }

  JITSymbol findMangledSymbol(const std::string &Name) {
#ifdef _WIN32
    // The symbol lookup of ObjectLinkingLayer uses the SymbolRef::SF_Exported
    // flag to decide whether a symbol will be visible or not, when we call
    // IRCompileLayer::findSymbolIn with ExportedSymbolsOnly set to true.
    //
    // But for Windows COFF objects, this flag is currently never set.
    // For a potential solution see: https://reviews.llvm.org/rL258665
    // For now, we allow non-exported symbols on Windows as a workaround.
    const bool ExportedSymbolsOnly = false;
#else
    const bool ExportedSymbolsOnly = true;
#endif

    // Search modules in reverse order: from last added to first added.
    // This is the opposite of the usual search order for dlsym, but makes more
    // sense in a REPL where we want to bind to the newest available definition.
    for (auto H : make_range(ModuleKeys.rbegin(), ModuleKeys.rend()))
      if (auto Sym = CompileLayer.findSymbolIn(H, Name, ExportedSymbolsOnly))
        return Sym;

    // If we can't find the symbol in the JIT, try looking in the host process.
    if (auto SymAddr = RTDyldMemoryManager::getSymbolAddressInProcess(Name))
      return JITSymbol(SymAddr, JITSymbolFlags::Exported);

#ifdef _WIN32
    // For Windows retry without "_" at beginning, as RTDyldMemoryManager uses
    // GetProcAddress and standard libraries like msvcrt.dll use names
    // with and without "_" (for example "_itoa" but "sin").
    if (Name.length() > 2 && Name[0] == '_')
      if (auto SymAddr =
              RTDyldMemoryManager::getSymbolAddressInProcess(Name.substr(1)))
        return JITSymbol(SymAddr, JITSymbolFlags::Exported);
#endif

    return nullptr;
  }

  ExecutionSession ES;
  std::shared_ptr<SymbolResolver> Resolver;
  std::unique_ptr<TargetMachine> TM;
  const DataLayout DL;
  ObjLayerT ObjectLayer;
  CompileLayerT CompileLayer;
  std::vector<VModuleKey> ModuleKeys;
};

} // end namespace orc
} // end namespace llvm

#endif // LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H

为方便起见,我提供了一个Makefile:

LLVM_CONFIG = ${LLVM_INSTALL_PATH}

LLVM_CXXFLAGS = $(shell $(LLVM_CONFIG) --cxxflags)
LLVM_LDFLAGS  = $(shell $(LLVM_CONFIG) --ldflags)
LLVM_LIBS     = $(shell $(LLVM_CONFIG) --libs)

all: test

test.o: test.cc KaleidoscopeJIT.h
    g++ -c -o $@ $< $(LLVM_CXXFLAGS)

test: test.o
    g++ -o $@ $< $(LLVM_LDFLAGS) $(LLVM_LIBS)

clean:
    rm -f *.o
    rm -f test

1 个答案:

答案 0 :(得分:0)

我相信可以在这里找到针对llvm 7和8的解决方案: https://stackoverflow.com/a/56862433/2310373

即,替换:

[this](const std::string &Name) {
    return ObjectLayer.findSymbol(Name, true);
},

类似

[this](const std::string &Name) {
    auto FoundSymbol = ObjectLayer.findSymbol(Name, true);
    if (!FoundSymbol) {
        if (auto SymAddr = RTDyldMemoryManager::getSymbolAddressInProcess(Name))
            return JITSymbol(SymAddr, JITSymbolFlags::Exported);
    }
    return FoundSymbol;
},