2016-08-30 7 views
7

最近、私はMCHITと共にLLVM(3.8.1)で動作するようにSEH例外処理を試みてきました。今のところ運がない。LLVM MCJIT/SEH例外処理

私がウェブサイト(http://llvm.org/docs/ExceptionHandling.html)から理解しているところから、これはかなり実装する必要があります。 clangで最小限のコードをコンパイルすると、同じLLVM IRコードが得られます。しかし、私が試してみると、プログラムは厄介なStack cookie instrumentation code detected a stack-based buffer overrun.でクラッシュします。私がしようとしてきたものを説明するために

、私は最低限のテストケース(私は、コードの量について謝罪...)作成しました:

#include <string> 
#include <iostream> 
#include <exception> 

#pragma warning(push) 
#pragma warning(disable: 4267) 
#pragma warning(disable: 4244) 
#pragma warning(disable: 4800) 
#pragma warning(disable: 4996) 
#pragma warning(disable: 4141) 
#pragma warning(disable: 4146) 
#pragma warning(disable: 4624) 
#pragma warning(disable: 4291) 

#define DONT_GET_PLUGIN_LOADER_OPTION 

#include "llvm/ADT/StringRef.h" 
#include "llvm/ADT/StringMap.h" 
#include "llvm/ADT/Triple.h" 
#include "llvm/PassRegistry.h" 
#include "llvm/IR/DataLayout.h" 
#include "llvm/IR/LegacyPassManager.h" 
#include "llvm/LinkAllPasses.h" 
#include "llvm/Analysis/Passes.h" 
#include "llvm/Analysis/TargetTransformInfo.h" 
#include "llvm/ExecutionEngine/ExecutionEngine.h" 
#include "llvm/ExecutionEngine/GenericValue.h" 
#include "llvm/ExecutionEngine/Interpreter.h" 
#include "llvm/ExecutionEngine/MCJIT.h" 
#include "llvm/IR/Constants.h" 
#include "llvm/IR/Type.h" 
#include "llvm/IR/LLVMContext.h" 
#include "llvm/IR/Module.h" 
#include "llvm/IR/Function.h" 
#include "llvm/IR/BasicBlock.h" 
#include "llvm/IR/IRBuilder.h" 
#include "llvm/IR/DerivedTypes.h" 
#include "llvm/IR/Value.h" 
#include "llvm/Support/Allocator.h" 
#include "llvm/Support/FormattedStream.h" 
#include "llvm/Support/ManagedStatic.h" 
#include "llvm/Support/PluginLoader.h" 
#include "llvm/Support/Host.h" 
#include "llvm/Support/PrettyStackTrace.h" 
#include "llvm/Support/Signals.h" 
#include "llvm/Support/SourceMgr.h" 
#include "llvm/Support/TargetRegistry.h" 
#include "llvm/Support/TargetSelect.h" 
#include "llvm/Support/ToolOutputFile.h" 
#include "llvm/Target/TargetMachine.h" 
#include "llvm/Transforms/Scalar.h" 
#include "llvm/Transforms/IPO/PassManagerBuilder.h" 

#pragma warning(pop) 

static void test() 
{ 
    // You can use this to see that function calls work fine. 
    // std::cout << "Foo!" << std::endl; 

    throw std::exception("Something we should try to catch."); 
} 

int main() 
{ 
    // Initialize LLVM 
    std::cout << "Initializing LLVM." << std::endl; 

    llvm::InitializeNativeTarget(); 
    llvm::InitializeAllTargetMCs(); 
    llvm::InitializeNativeTargetAsmPrinter(); 
    llvm::InitializeNativeTargetAsmParser(); 

    llvm::PassRegistry *Registry = llvm::PassRegistry::getPassRegistry(); 
    llvm::initializeCore(*Registry); 
    llvm::initializeScalarOpts(*Registry); 
    llvm::initializeObjCARCOpts(*Registry); 
    llvm::initializeVectorization(*Registry); 
    llvm::initializeIPO(*Registry); 
    llvm::initializeAnalysis(*Registry); 
    llvm::initializeTransformUtils(*Registry); 
    llvm::initializeInstCombine(*Registry); 
    llvm::initializeInstrumentation(*Registry); 
    llvm::initializeTarget(*Registry); 
    // For codegen passes, only passes that do IR to IR transformation are 
    // supported. 
    llvm::initializeCodeGenPreparePass(*Registry); 
    llvm::initializeAtomicExpandPass(*Registry); 
    llvm::initializeRewriteSymbolsPass(*Registry); 
    llvm::initializeWinEHPreparePass(*Registry); 
    llvm::initializeDwarfEHPreparePass(*Registry); 
    llvm::initializeSjLjEHPreparePass(*Registry); 

    llvm::StringRef MCPU = llvm::sys::getHostCPUName(); 
    std::string MTrip = llvm::sys::getProcessTriple(); 

    static llvm::StringMap<bool, llvm::MallocAllocator> features; 
    llvm::sys::getHostCPUFeatures(features); 

    // Initialize module & context: 
    auto context = std::unique_ptr<llvm::LLVMContext>(new llvm::LLVMContext()); 
    auto module = std::unique_ptr<llvm::Module>(new llvm::Module("native", *context)); 

    // Create 'main' method: 

    llvm::Type* returnType = llvm::Type::getInt32Ty(*context); 
    std::vector<llvm::Type*> arguments; 

    // MCJIT only supports main(int, char**) 
    arguments.push_back(llvm::Type::getInt32Ty(*context)); 
    arguments.push_back(llvm::Type::getInt8PtrTy(*context)->getPointerTo()); 

    llvm::Function *fcn = llvm::cast<llvm::Function>(module->getOrInsertFunction("main", llvm::FunctionType::get(returnType, arguments, false))); 

    // Generate exception handler info for main: 
    llvm::AttrBuilder argBuilder; 
    argBuilder.addAttribute(llvm::Attribute::UWTable); 
    argBuilder.addAttribute("stack-protector-buffer-size", "8"); 
    fcn->addAttributes(llvm::AttributeSet::FunctionIndex, llvm::AttributeSet::get(*context, llvm::AttributeSet::FunctionIndex, argBuilder)); 

    // Exception handling requires a personality function. We want to use the SEH personality handler 
    llvm::Function *personalityHandler = llvm::cast<llvm::Function>(module->getOrInsertFunction("__CxxFrameHandler3", llvm::FunctionType::get(llvm::Type::getInt32Ty(*context), true))); 
    auto personalityPtr = llvm::ConstantExpr::getBitCast(personalityHandler, llvm::Type::getInt8PtrTy(*context)); 
    fcn->setPersonalityFn(personalityPtr); 

    // Create some code. Basically we want to invoke our 'test' method 
    auto block = llvm::BasicBlock::Create(*context, "code", fcn); 
    llvm::IRBuilder<> builder(block); 

    // all other cases might throw an exception 
    auto continueBlock = llvm::BasicBlock::Create(*context, "invoke.cont", fcn); 
    auto catchDispatch = llvm::BasicBlock::Create(*context, "catch.dispatch", fcn); 

    // Register 'test' as an external function: 
    const void* testFunctionPtr = &test; 
    auto testFunctionType = llvm::FunctionType::get(builder.getVoidTy(), false); 
    auto testFunction = llvm::Function::Create(testFunctionType, llvm::Function::ExternalLinkage, "test", module.get()); 

    // %call = invoke i32 @"test"() to label %invoke.cont unwind label %catch.dispatch 
    auto call = builder.CreateInvoke(testFunction, continueBlock, catchDispatch); 

    // return [ 0 from ok, 1 from catch handler ] 
    builder.SetInsertPoint(continueBlock); 
    auto phi = builder.CreatePHI(builder.getInt32Ty(), 2, "result"); 
    phi->addIncoming(builder.getInt32(0), block); 
    builder.CreateRet(phi); 

    // Create exception handler: 

    // Create default catch block. Basically handles the exception and returns '1'. 
    builder.SetInsertPoint(catchDispatch); 
    auto parentPad = llvm::ConstantTokenNone::get(*context); 
    // %0 = catchswitch within none [label %catch] unwind to caller 
    auto catchSwitch = builder.CreateCatchSwitch(parentPad, nullptr, 1); 

    auto catchBlock = llvm::BasicBlock::Create(*context, "catch", fcn); 
    builder.SetInsertPoint(catchBlock); 
    catchSwitch->addHandler(catchBlock); 

    // MSVC code: 
    // %1 = catchpad within %0 [i8* null, i32 64, i8* null] == "catch all" 
    llvm::Value *nullPtr = llvm::ConstantPointerNull::get(llvm::Type::getInt8PtrTy(*context)); 
    auto catchPad = builder.CreateCatchPad(catchSwitch, { nullPtr, builder.getInt32(0x40), nullPtr }); 

    // catchret from %1 to label %return 
    auto const1 = builder.getInt32(1); 
    builder.CreateCatchRet(catchPad, continueBlock); 

    // set 1 for the catch handler 
    phi->addIncoming(builder.getInt32(1), catchBlock); 

    // *DONE* building the code. 

    // Dump the LLVM IR: 
    module->dump(); 

    // Let's JIT the code: 

    std::string error; 
    auto trip = llvm::Triple::normalize(MTrip); 
    llvm::Triple triple(trip); 
    const llvm::Target *target = llvm::TargetRegistry::lookupTarget("x86-64", triple, error); 
    if (!target) 
    { 
     throw error.c_str(); 
    } 

    llvm::TargetOptions Options; 

    std::unique_ptr<llvm::TargetMachine> targetMachine(
     target->createTargetMachine(trip, MCPU, "", Options, llvm::Reloc::Default, llvm::CodeModel::Default, llvm::CodeGenOpt::Aggressive)); 

    if (!targetMachine.get()) 
    { 
     throw "Could not allocate target machine!"; 
    } 

    // Create the target machine; set the module data layout to the correct values. 
    auto DL = targetMachine->createDataLayout(); 
    module->setDataLayout(DL); 
    module->setTargetTriple(trip); 

    // Pass manager builder: 
    llvm::PassManagerBuilder pmbuilder; 
    pmbuilder.OptLevel = 3; 
    pmbuilder.BBVectorize = false; 
    pmbuilder.SLPVectorize = true; 
    pmbuilder.LoopVectorize = true; 
    pmbuilder.Inliner = llvm::createFunctionInliningPass(3, 2); 
    llvm::TargetLibraryInfoImpl *TLI = new llvm::TargetLibraryInfoImpl(triple); 
    pmbuilder.LibraryInfo = TLI; 

    // Generate pass managers: 

    // 1. Function pass manager: 
    llvm::legacy::FunctionPassManager FPM(module.get()); 
    pmbuilder.populateFunctionPassManager(FPM); 

    // 2. Module pass manager: 
    llvm::legacy::PassManager PM; 
    PM.add(llvm::createTargetTransformInfoWrapperPass(targetMachine->getTargetIRAnalysis())); 
    pmbuilder.populateModulePassManager(PM); 

    // 3. Execute passes: 
    // - Per-function passes: 
    FPM.doInitialization(); 
    for (llvm::Module::iterator I = module->begin(), E = module->end(); I != E; ++I) 
    { 
     if (!I->isDeclaration()) 
     { 
      FPM.run(*I); 
     } 
    } 
    FPM.doFinalization(); 

    // - Per-module passes: 
    PM.run(*module); 


    // All done, *RUN*. 
    llvm::EngineBuilder engineBuilder(std::move(module)); 
    engineBuilder.setEngineKind(llvm::EngineKind::JIT); 
    engineBuilder.setMCPU(MCPU); 
    engineBuilder.setMArch("x86-64"); 
    engineBuilder.setUseOrcMCJITReplacement(false); 
    engineBuilder.setOptLevel(llvm::CodeGenOpt::None); 

    llvm::ExecutionEngine* engine = engineBuilder.create(); 

    // Register global 'test' function: 
    engine->addGlobalMapping(testFunction, const_cast<void*>(testFunctionPtr)); // Yuck... 

    // Finalize 
    engine->finalizeObject(); 

    // Invoke: 
    std::vector<llvm::GenericValue> args(2); 
    args[0].IntVal = llvm::APInt(32, static_cast<uint64_t>(0), true); 
    args[1].PointerVal = nullptr; 

    llvm::GenericValue gv = engine->runFunction(fcn, args); 
    auto result = int(gv.IntVal.getSExtValue()); 

    std::cout << "Result after execution: " << result << std::endl; 

    std::string s; 
    std::getline(std::cin, s); 
} 

をこれには、次のIRコードを生成します。

; ModuleID = 'native' 

; Function Attrs: uwtable 
define i32 @main(i32, i8**) #0 personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) { 
code: 
    invoke void @test() 
      to label %invoke.cont unwind label %catch.dispatch 

invoke.cont:          ; preds = %catch, %code 
    %result = phi i32 [ 0, %code ], [ 1, %catch ] 
    ret i32 %result 

catch.dispatch:         ; preds = %code 
    %2 = catchswitch within none [label %catch] unwind to caller 

catch:           ; preds = %catch.dispatch 
    %3 = catchpad within %2 [i8* null, i32 64, i8* null] 
    catchret from %3 to label %invoke.cont 
} 

declare i32 @__CxxFrameHandler3(...) 

declare void @test() 

attributes #0 = { uwtable "stack-protector-buffer-size"="8" } 

質問:私は何が分からないのですが、これはどうして解決できないのですか?

+0

このスタックオーバーフローの原因となったコードは何ですか? –

+0

@ShmuelHazanスタックオーバーフローではありません。そしてその詳細は、この問題の原因を明らかにする。また、私がそれを理解したら、明らかにそれに恩恵を与えませんでした。 – atlaste

答えて

1

この問題をllvmデベロッパーリストに投稿した後、この問題が既知のバグ:https://llvm.org/bugs/show_bug.cgi?id=24233にどのように関連しているかを説明しています。

基本的には、LLVMはWindows(具体的にはSEHとデバッグ)がスタックフレームを処理するために必要なコードを実装していません。私はこの主題の専門家ではありませんが、これが実装されるまで、SEHは何をすべきか分からないでしょう。つまり、C++例外は基本的にうまくいきません。

もちろん明白な回避策は、関数呼び出し中にオブジェクトをポインタとして渡し、if-then-elseを実行することです。そうすれば、例外は回避されます。しかし、これはかなり厄介で、おそらく深刻なパフォーマンスの低下をもたらすでしょう。また、これにより、コンパイラおよび生成されたプログラムの流れがずっと複雑になります。換言すれば、私はむしろそうではないと言いましょう。

私は質問を開いたままにします。誰かがハックを見つけたり回避策を見つけたりしたら、私はそれをうれしく受け入れます。