c++ - Generate assembly from C code in memory using libclang -
i need implement library compiles c code ebpf bytecode using llvm/clang backend. codes read memory , need resultant assembly code in memory too.
until now, have been able compile llvm ir using following code:
#include <string> #include <vector> #include <clang/frontend/compilerinstance.h> #include <clang/basic/diagnosticoptions.h> #include <clang/frontend/textdiagnosticprinter.h> #include <clang/codegen/codegenaction.h> #include <clang/basic/targetinfo.h> #include <llvm/support/targetselect.h> using namespace std; using namespace clang; using namespace llvm; int main() { constexpr auto testcodefilename = "test.cpp"; constexpr auto testcode = "int test() { return 2+2; }"; // prepare compilation arguments vector<const char *> args; args.push_back(testcodefilename); // prepare diagnosticengine diagnosticoptions diagopts; textdiagnosticprinter *textdiagprinter = new clang::textdiagnosticprinter(errs(), &diagopts); intrusiverefcntptr<clang::diagnosticids> pdiagids; diagnosticsengine *pdiagnosticsengine = new diagnosticsengine(pdiagids, &diagopts, textdiagprinter); // initialize compilerinvocation compilerinvocation *ci = new compilerinvocation(); compilerinvocation::createfromargs(*ci, &args[0], &args[0] + args.size(), *pdiagnosticsengine); // map code filename memorybuffer stringref testcodedata(testcode); unique_ptr<memorybuffer> buffer = memorybuffer::getmembuffercopy(testcodedata); ci->getpreprocessoropts().addremappedfile(testcodefilename, buffer.get()); // create , initialize compilerinstance compilerinstance clang; clang.setinvocation(ci); clang.creatediagnostics(); // set target (i guess can initialize bpf target, don't know how) initializealltargets(); const std::shared_ptr<clang::targetoptions> targetoptions = std::make_shared<clang::targetoptions>(); targetoptions->triple = string("bpf"); targetinfo *ptargetinfo = targetinfo::createtargetinfo(*pdiagnosticsengine,targetoptions); clang.settarget(ptargetinfo); // create , execute action // codegenaction *compileraction = new emitllvmonlyaction(); codegenaction *compileraction = new emitassemblyaction(); clang.executeaction(*compileraction); buffer.release(); }
to compile use following cmakelists.txt:
cmake_minimum_required(version 3.3.2) project(clang_backend cxx) set(cmake_cxx_compiler "clang++") execute_process(command llvm-config --cxxflags output_variable llvm_config output_strip_trailing_whitespace) execute_process(command llvm-config --libs output_variable llvm_libs output_strip_trailing_whitespace) set(cmake_cxx_flags ${llvm_config}) set(clang_libs clang clangfrontend clangdriver clangserialization clangparse clangcodegen clangsema clanganalysis clangedit clangast clanglex clangbasic ) add_executable(clang_backend main.cpp) target_link_libraries(clang_backend ${clang_libs}) target_link_libraries(clang_backend ${llvm_libs})
if understood correctly, should able generate assembly code if change compiler action emitassemblyaction(), i'm not initializing i'm getting segmentation fault in llvm::targetpassconfig::addpassestohandleexceptions (this=this@entry=0x6d8d30) @ /tmp/llvm-3.7.1.src/lib/codegen/passes.cpp:419
the code @ line is:
switch (tm->getmcasminfo()->getexceptionhandlingtype()) {
does have example or knows i'm missing?
so, if compile llvm asserts on, error clearer, , tell need do:
x: .../src/llvm/lib/codegen/llvmtargetmachine.cpp:63: void llvm::llvmtargetmachine::initasminfo(): assertion `tmpasminfo && "mcasminfo not initialized. " "make sure include correct targetselect.h" "and initializealltargetmcs() being invoked!"' failed.
(i added line-breaks that, since printed single long line).
after adding required initializealltargetmcs()
@ beginning of main
, got error. looking @ object file generation of compiler, "guessed" problem initializeall*
call. little bit of testing, , turns out need initializeallasmprinters();
- makes sense given want produce assembly code.
i'm not entirely sure how "see" results code, adding 2 beginning of main
makes run completion rather assert, exit error or crash - typically step in right direction.
so main
looks in "my" code:
int main() { constexpr auto testcodefilename = "test.cpp"; constexpr auto testcode = "int test() { return 2+2; }"; initializealltargetmcs(); initializeallasmprinters(); // prepare compilation arguments vector<const char *> args; args.push_back(testcodefilename); // prepare diagnosticengine diagnosticoptions diagopts; textdiagnosticprinter *textdiagprinter = new clang::textdiagnosticprinter(errs(), &diagopts); intrusiverefcntptr<clang::diagnosticids> pdiagids; diagnosticsengine *pdiagnosticsengine = new diagnosticsengine(pdiagids, &diagopts, textdiagprinter); // initialize compilerinvocation compilerinvocation *ci = new compilerinvocation(); compilerinvocation::createfromargs(*ci, &args[0], &args[0] + args.size(), *pdiagnosticsengine); // map code filename memorybuffer stringref testcodedata(testcode); unique_ptr<memorybuffer> buffer = memorybuffer::getmembuffercopy(testcodedata); ci->getpreprocessoropts().addremappedfile(testcodefilename, buffer.get()); // create , initialize compilerinstance compilerinstance clang; clang.setinvocation(ci); clang.creatediagnostics(); // set target (i guess can initialize bpf target, don't know how) initializealltargets(); const std::shared_ptr<clang::targetoptions> targetoptions = std::make_shared<clang::targetoptions>(); targetoptions->triple = string("bpf"); targetinfo *ptargetinfo = targetinfo::createtargetinfo(*pdiagnosticsengine,targetoptions); clang.settarget(ptargetinfo); // create , execute action // codegenaction *compileraction = new emitllvmonlyaction(); codegenaction *compileraction = new emitassemblyaction(); clang.executeaction(*compileraction); buffer.release(); }
i suggest if want develop clang&llvm, build debug version of clang&llvm - both in tracking down "why" , catch problems , more obvious. use -dcmake_build_type=debug
cmake
flavour.
my complete script getting llvm & clang build:
export cc=clang export cxx=clang++ cmake -dcmake_build_type=debug -dcmake_install_prefix=/usr/local/llvm-debug -dllvm_tar gets_to_build=x86 ../llvm
[i using late pre-release of 3.8 test this, doubt it's different 3.7.1 in respect]
Comments
Post a Comment