[LLVMdev] Crash using the JIT on x86 but work on x64

Skykill Skyoverside skyoverside at live.fr
Fri Jul 6 19:18:10 PDT 2012


Hello everyone, i’m using LLVM (updated to 3.1 after seeing that bug, but it’s the same with 3.0) for running a bitcode on a C++ program, and Clang for compiling it. My code work perfectly, as expected on x64, but crash on x86. I’m on Windows 7 x64 and LLVM + Clang was compiled using Visual Studio 2010 (tested in both Release and Debug build). Project was make using CMake.

Here is my code:

main.cpp:


#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
//#include "llvm/Type.h"
//#include "llvm/ADT/Triple.h"
//#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/ExecutionEngine/GenericValue.h"
//#include "llvm/ExecutionEngine/Interpreter.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/ExecutionEngine/JITMemoryManager.h"
//#include "llvm/ExecutionEngine/MCJIT.h"
//#include "llvm/Support/CommandLine.h"
#include "llvm/Support/IRReader.h"
//#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
//#include "llvm/Support/PluginLoader.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/raw_ostream.h"
//#include "llvm/Support/Process.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
//#include <cerrno>

#include "llvm/DerivedTypes.h"
//#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Linker.h"

//using namespace llvm;


llvm::LLVMContext* LLVM_Context;
static llvm::ExecutionEngine* LLVM_ExecE = 0;
llvm::Module* LLVM_Module;
llvm::Type* LLVM_pointerType;

bool loadChunk(void* chunk)
{
    llvm::Module* m = (llvm::Module*)chunk;
    std::string msg;

    if (llvm::Linker::LinkModules(LLVM_Module, m, llvm::Function::ExternalLinkage, &msg))
    {
        printf("\nLINK ERROR - %s", &msg);
    }
    return true;
}
bool loadFile(const char* filename)
{
    // Load the bitcode...
    llvm::SMDiagnostic Err;
    llvm::Module* Mod = ParseIRFile(filename, Err, *LLVM_Context);
    if (!Mod) {
        Err.print("Error loading bitcode", llvm::errs());
        return 1;
    }
    printf("\nLink with main module.\n");
    loadChunk(Mod);

    printf("\nDone loading %s", filename);
    return true;
}
void callFunction(const char* name, unsigned int argc, ...)
{
    llvm::Function *f = LLVM_Module->getFunction(name);
    if (f)
    {
        std::vector<llvm::GenericValue> args;
        if (argc > 0) 
        {
            va_list argv;

            va_start(argv, argc);
            for (unsigned int x = 0; x < argc; x++) {
                llvm::GenericValue v;
                v.DoubleVal = (va_arg(argv, double)); // this might not work for anything other than doubles!
                args.push_back(v);
            }
            va_end(argv);
        }
        LLVM_ExecE->runFunction(f, args);
    }
    else 
    {
        printf("\nTried to execute non-existent function '%c'.\n", name);
    }
}

template<typename T>
const static void* void_cast(const T& object)
{
    union Retyper
    {
        const T object;
        void* pointer;
        Retyper(T obj) : object(obj) { }
    };

    return Retyper(object).pointer;
}
template<typename T, typename M>
const static void* getMethodPointer(const T* object, M method) // will work for virtual methods
{
    union MethodEntry
    {
        intptr_t offset;
        void* function;
    };

    const MethodEntry* entry = static_cast<const MethodEntry*>(void_cast(&method));

    if (entry->offset % sizeof(intptr_t) == 0) // looks like that's how the runtime guesses virtual from static
        return getMethodPointer(method);

    const void* const* const vtable = *reinterpret_cast<const void* const* const* const>(object);
    return vtable[(entry->offset - 1) / sizeof(void*)];
}
template<typename M>
const static void* getMethodPointer(M method) // will only work with non-virtual methods
{
    union MethodEntry
    {
        intptr_t offset;
        void* function;
    };

    return static_cast<const MethodEntry*>(void_cast(&method))->function;
}

static llvm::Type* voidptr_type = NULL;

void registerSymbolFunction(const char* mangled_name, void* func)
{
    // This is the JIT way for register a function
    // Commented "sig.push_back" before defined function because they don't seem to need args
    std::vector<llvm::Type*> sig;
    llvm::FunctionType* FT = NULL;
    llvm::Function* F = NULL;

    sig.clear();
    //sig.push_back(LLVM_pointerType);
    // Get the function type
    FT = llvm::FunctionType::get(voidptr_type, sig, false);
    // Create the function
    F = llvm::Function::Create(FT, llvm::Function::ExternalLinkage, mangled_name, LLVM_Module);
    // Register the function to the global module
    // This method register the function to the module and the ExecutionEngine
    LLVM_ExecE->addGlobalMapping(F, func);

    // This also work, i don't know what method is better, addGlobalMapping seem to be for the JIT
    // and AddSymbol for the interpreter
    // This method register the function directly to LLVM
    //llvm::sys::DynamicLibrary::AddSymbol(mangled_name, func);
}

std::string getPrintText()
{
    printf("\nRunning getPrintText()...\n");
    return std::string("Hello world from getPrintText()!");
}
class TestClass1
{
public:
    static void test1()
    {
        printf("\nHello World from TestClass1::test1()!\n");
    }
    void test2(std::string s)
    {
        printf("\nHello World from TestClass1::test2()! : %s\n",+s.data());
        //ConsoleM.newMsg(S+"Hello World from TestClass1::test2()! : "+s.data(), YELLOW);
    }
} testclass1;

int main(int argc, char** argv, char* const* envp)
{
    printf("argc: %i ", argc);
    printf("argv: %c ", argv);
    printf("envp: %c ", envp);

    llvm::sys::PrintStackTraceOnErrorSignal();
    llvm::PrettyStackTraceProgram X(argc, argv);

    LLVM_Context = new llvm::LLVMContext();

    // If we have a native target, initialize it to ensure it is linked in and
    // usable by the JIT.
    llvm::InitializeNativeTarget();
    llvm::InitializeNativeTargetAsmPrinter();

    llvm::SMDiagnostic Err;
    LLVM_Module = new llvm::Module("LLVM Test", *LLVM_Context);
    if (!LLVM_Module) {
        Err.print(argv[0], llvm::errs());
        return 1;
    }

    std::string ErrorMsg;
    llvm::EngineBuilder builder(LLVM_Module);
    builder.setErrorStr(&ErrorMsg);
    builder.setJITMemoryManager(llvm::JITMemoryManager::CreateDefaultMemManager());
    builder.setEngineKind(llvm::EngineKind::JIT);

    //CodeGenOpt::Level OLvl = CodeGenOpt::Default;

    //builder.setOptLevel(OLvl);

    llvm::TargetOptions Options;
    Options.JITExceptionHandling = false;
    Options.JITEmitDebugInfo = false;
    Options.JITEmitDebugInfoToDisk = false;
    builder.setTargetOptions(Options);

    LLVM_ExecE = builder.create();
    if (!LLVM_ExecE) {
        if (!ErrorMsg.empty())
            llvm::errs() << argv[0] << ": error creating ExecE: " << ErrorMsg << "\n";
        else
            llvm::errs() << argv[0] << ": unknown error creating ExecE!\n";
        exit(1);
    }

    // The following functions have no effect if their respective profiling
    // support wasn't enabled in the build configuration.
    LLVM_ExecE->RegisterJITEventListener(
        llvm::JITEventListener::createOProfileJITEventListener());
    LLVM_ExecE->RegisterJITEventListener(
        llvm::JITEventListener::createIntelJITEventListener());

    LLVM_ExecE->DisableLazyCompilation(false);


    voidptr_type = llvm::IntegerType::get(*LLVM_Context, 8)->getPointerTo();

    LLVM_pointerType = (llvm::Type*)llvm::Type::getInt32Ty(*LLVM_Context);


    registerSymbolFunction("_Z12getPrintTextv", (void*)&getPrintText);

    registerSymbolFunction("_ZdlPv", (void*)(void(*) (void*))operator delete);

    registerSymbolFunction("_ZN10TestClass15test2ESs", (void*)getMethodPointer(&TestClass1::test2));

    //registerSymbolFunction("_ZN10TestClass15test1Ev", (void *)TestClass1::test1);


    // Run static constructors.
    LLVM_ExecE->runStaticConstructorsDestructors(LLVM_Module, false);

    loadFile("engine_test.bc");

    // Run main.
    std::vector<llvm::GenericValue> args;
    LLVM_ExecE->runFunction(LLVM_Module->getFunction("_Z4Initv"), args);

    // Run static destructors.
    LLVM_ExecE->runStaticConstructorsDestructors(true);

    return 1;
}


engine_test.cpp:


#include <string>

std::string getPrintText();

class TestClass1
{
public:
    static void test1();
    void test2(std::string s);
} testclass1;

bool Init()
{
    printf("\n\nStarting bitcode...\n");

    //This work on x86 and x64 using LLVM in Release, but crash in x86 Debug
    printf("Result from getPrintText(): %s", getPrintText().data());
    printf("\ngetPrintText() finished!\n");

    //This work on x64 using LLVM in Release, but crash in x86 both Release and Debug
    //testclass1.test2(std::string("HI! testclass1"));
    TestClass1 _testclass1;
    _testclass1.test2(std::string("Hello world!"));

    printf("\nEnding bitcode...\n\n");
    return true;
}


And the command i’m using for compiling the bitcode:

clang++ -v -O3 -I "../CodeHeaders/VS2010/include" -std=c++11 -emit-llvm "../engine_test.cpp" -c -o engine_test.bc

>From what i have see it could be a bug with the "getMethodPointer" function (i’m using it for getting the address of the function in a class). Or with the "delete" operator from what i could see on the Visual Studio call stack. Basicaly "_testclass1.test2" is executed, but crash just after, so probably when he get deleted.

After trying to test my code with the x64 Debug build i have found that it also crash, but the call stack is different then the x86 Debug build.

Call stack using LLVM in x86 Debug:

     ntdll.dll!778515de()     
     [Les frames ci-dessous sont peut-être incorrects et/ou manquants, aucun symbole chargé pour ntdll.dll]    
     ntdll.dll!778515de()     
     ntdll.dll!7784014e()     
>    msvcr100d.dll!_write(int fh, const void * buf, unsigned int cnt)  Ligne 83 + 0x9 octets    C
     msvcr100d.dll!_write(int fh, const void * buf, unsigned int cnt)  Ligne 82 + 0xc octets    C
     013df8f4()    
     msvcp100d.dll!std::_Container_base12::_Orphan_all()  Ligne 200    C++
     LLVM_Test_Console.exe!std::_String_val<char,std::allocator<char> >::~_String_val<char,std::allocator<char> >()  Ligne 478 + 0xb octets    C++
     LLVM_Test_Console.exe!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> >()  Ligne 754 + 0xf octets    C++
     LLVM_Test_Console.exe!TestClass1::test2(std::basic_string<char,std::char_traits<char>,std::allocator<char> > s)  Ligne 172 + 0xf octets    C++
     001e0091()    
     LLVM_Test_Console.exe!llvm::JIT::runFunction(llvm::Function * F, const std::vector<llvm::GenericValue,std::allocator<llvm::GenericValue> > & ArgValues)  Ligne 455 + 0x7 octets    C++
     LLVM_Test_Console.exe!main(int argc, char * * argv, char * const * envp)  Ligne 254 + 0x43 octets    C++
     LLVM_Test_Console.exe!__tmainCRTStartup()  Ligne 555 + 0x19 octets    C
     LLVM_Test_Console.exe!mainCRTStartup()  Ligne 371    C
     kernel32.dll!76dd339a()     
     ntdll.dll!77869ef2()     
     ntdll.dll!77869ec5()     

Call stack using LLVM in x86 Release:

     ntdll.dll!778515de()     
     [Les frames ci-dessous sont peut-être incorrects et/ou manquants, aucun symbole chargé pour ntdll.dll]    
     ntdll.dll!778515de()     
     ntdll.dll!7784014e()     
     msvcr100.dll!56a7a5d0()     
     msvcr100.dll!56a70949()     
     msvcr100.dll!56a7f00d()     
     kernel32.dll!76dd14dd()     
     msvcr100.dll!56a7016a()     
     LLVM_Test_Console.exe!TestClass1::test2(std::basic_string<char,std::char_traits<char>,std::allocator<char> > s)  Ligne 172 + 0x10 octets    C++
     00160091()    
>    LLVM_Test_Console.exe!llvm::JIT::runFunction()  + 0x2ed octets    C++
     LLVM_Test_Console.exe!main(int argc, char * * argv, char * const * envp)  Ligne 254 + 0x41 octets    C++
     msvcr100.dll!56a7263d()     
     kernel32.dll!76dd339a()     
     ntdll.dll!77869ef2()     
     ntdll.dll!77869ec5()     

Call stack using LLVM in x64 Debug:

>    msvcr100d.dll!_output_l(_iobuf * stream, const char * format, localeinfo_struct * plocinfo, char * argptr)  Ligne 1644 + 0x23 octets    C++
     msvcr100d.dll!printf(const char * format, ...)  Ligne 62 + 0x1e octets    C
     LLVM_Test_Console.exe!TestClass1::test2(std::basic_string<char,std::char_traits<char>,std::allocator<char> > * s)  Ligne 170 + 0x1a octets    C++
     00000000003500c7()    
     00000000001eed78()    
     00000000001eed50()    
     00000000003e00d0()    

Realy hope you could help me, since i don’t know if it’s bug in my code or in LLVM. :).

Thanks you.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20120707/1e702f69/attachment.html>


More information about the llvm-dev mailing list