I'm trying to load an LLVM module defined in a .bc
file at runtime but have run into a snag.
The bitcode of interest has been generated from hello.cpp
:
// hello.cpp
// build with:
// clang-3.4 -c -emit-llvm hello.cpp -o hello.bc
#include <iostream>
void hello()
{
std::cout << "Hello, world!" << std::endl;
}
When the program below attempts to load it at runtime, it crashes inside llvm::BitstreamCursor::Read()
:
// main.cpp
// build with:
// g++ main.cpp `llvm-config-3.4 --cppflags --ldflags --libs` -ldl -lpthread -lcurses
#include <llvm/IR/Module.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/raw_ostream.h>
#include <fstream>
#include <iostream>
llvm::Module *load_module(std::ifstream &stream)
{
if(!stream)
{
std::cerr << "error after open stream" << std::endl;
return 0;
}
// load bitcode
std::string ir((std::istreambuf_iterator<char>(stream)), (std::istreambuf_iterator<char>()));
// parse it
using namespace llvm;
LLVMContext context;
SMDiagnostic error;
Module *module = ParseIR(MemoryBuffer::getMemBuffer(StringRef(ir.c_str())), error, context);
if(!module)
{
std::string what;
llvm::raw_string_ostream os(what);
error.print("error after ParseIR()", os);
std::cerr << what;
} // end if
return module;
}
int main()
{
std::ifstream stream("hello.bc", std::ios_base::binary);
llvm::Module *m = load_module(stream);
if(m)
{
m->dump();
}
return 0;
}
I'm building against LLVM v3.4 using the command lines mentioned in the comments.
Any idea what I'm doing wrong?
There are two problems:
LLVMContext
needs to outlast the lifetime of the Module
. Otherwise the Module
will refer to a LLVMContext
which no longer exists.StringRef
referring to the IR should be constructed from the std::string
containing the IR, not a zero-terminated string. Otherwise ParseIR
won't find the end of the IR correctly.Here's the corrected version of load_module
:
llvm::Module *load_module(std::ifstream &stream, llvm::LLVMContext &context)
{
if(!stream)
{
std::cerr << "error after open stream" << std::endl;
return 0;
}
// load bitcode
std::string ir((std::istreambuf_iterator<char>(stream)), (std::istreambuf_iterator<char>()));
// parse it
using namespace llvm;
SMDiagnostic error;
Module *module = ParseIR(MemoryBuffer::getMemBuffer(StringRef(ir)), error, context);
if(!module)
{
std::string what;
llvm::raw_string_ostream os(what);
error.print("error after ParseIR()", os);
std::cerr << what;
} // end if
return module;
}
One possible solution is to replace the ParseIR()
call with
ParseIRFile("hello.bc", error, getGlobalContext())
Doing this also makes the code a lot simpler since you can skip the part where you try to manually load the bitcode.
The following is sufficient to get a dump for the module (you should add in some error handling to it)
#include <llvm/IR/Module.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/Support/SourceMgr.h>
using namespace llvm;
int main()
{
LLVMContext context;
SMDiagnostic error;
Module *m = ParseIRFile("hello.bc", error, context);
if(m)
{
m->dump();
}
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With