2016-08-30 9 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.와 충돌합니다. 이것은 다음 IR 코드를 생성

#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); 
} 

:

내가 할 시도 된 한 내용을 설명하기 위해

, 나는 최소한의 테스트 케이스를 (내가 ... 코드의 양에 대해 사과) 만들었습니다 :

; 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" } 

Q : 무엇이 누락 되었습니까?

+0

이 스택 오버플로를 일으킨 코드를 발견 했습니까? –

+0

@ShmuelHazan 스택 오버플로가 아닙니다. 세부적인 내용을 통해 문제의 원인을 알 수 있습니다. 또한, 만약 내가 알아 냈어, 나는 분명히 현상금을 부과하지 않을거야. – atlaste

답변

1

llvm 개발자 목록에이 문제를 게시 한 후이 문제가 알려진 버그 : https://llvm.org/bugs/show_bug.cgi?id=24233과 어떤 관련이 있는지 설명하는 친절한 답변을 받았습니다.

기본적으로 LLVM은 Windows (특히 SEH 및 디버깅)에서 스택 프레임을 처리하는 데 필요한 코드를 구현하지 않습니다. 나는이 주제에 대해 전문가가 아니지만, 이것이 구현 될 때까지 SEH는 무엇을해야할지 모를 것이다. 이는 C++ 예외가 기본적으로 작동하지 않는다는 것을 의미한다.

물론 명백한 해결 방법은 함수 호출 중에 객체를 포인터로 전달하고 if-then-else를 수행하는 것입니다. 그렇게하면 예외가 발생하지 않습니다. 그러나 이것은 꽤 심해서 심각한 성능 저하를 초래할 수 있습니다. 또한, 이것은 컴파일러와 생성 된 프로그램의 흐름을 훨씬 더 복잡하게 만듭니다. 다른 말로하면, 나는 단지 오히려하지 말라고 말하자.

질문을 공개하겠습니다. 누군가 해킹을 발견하거나 해결 방법을 찾으면 기꺼이 받아 들일 것입니다.