wai1216 发表于 2018-5-20 10:02:09

对Windows下的LLVM之把pass抽离到DLL中的学习以及优化

本帖最后由 wai1216 于 2018-5-20 10:24 编辑

原文如下:
Windows下的LLVM之把pass抽离到DLL中

步骤1 创建以及编译DLL(与原文相同)
创建一个工程的文件夹,例如:MyCustom
在目录下再创建一个src,作为我们的源码目录,把所写pass放入其中

MyPass.cpp
MyPass.h
MyCustom.cpp
MyCustom.h

代码如下
MyPass.h:
#ifndef __MYPASS_H
#define __MYPASS_H


// LLVM include
#define DEBUG_TYPE "mypass"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/PassSupport.h"


using namespace llvm;

// Namespace
namespace llvm
{
      FunctionPass *createMyPassPass(bool flag);
}
#endif __MYPASS_H


MyPass.cpp:
#include "MyPass.h"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/InitializePasses.h"

namespace
{
      struct MyPass : public FunctionPass
      {
                static char ID;
                MyPass() : FunctionPass(ID) {}

                bool runOnFunction(Function &F)
                {
                        errs() << "Hello,MyPass";
                        errs().write_escaped(F.getName()) << "\n";
                        return false;
                }
      };
}

char MyPass::ID = 0;
static RegisterPass<MyPass> X("mypass", "Print all function names",
      false /* Only looks at CFG */,
      false /* Analysis Pass */);

FunctionPass *llvm::createMyPassPass(bool flag)
{
      return new MyPass();
}

MyCustom.h:
#ifndef __MY_CUSTOM_H
#define __MY_CUSTOM_H

#include "MyPass.h"

namespace llvm
{
      // 在里面创建其他多个pass
      FunctionPass *createMyPasses(bool flag);
}

extern "C"
{
      // 在clang里根据配置创建自定义pass,called by PassManagerBuilder::populateModulePassManager
      __declspec(dllexport)void __stdcall clangAddCustomPass(legacy::PassManagerBase &MPM);
}

#endif __MY_CUSTOM_H

MyCustom.cpp:
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ManagedStatic.h"

#include "llvm/InitializePasses.h"
#include "llvm-c/Initialization.h"
#include "llvm/InitializePasses.h"
#include "llvm/PassRegistry.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LegacyPassManagers.h"


#include "MyCustom.h"

using namespace llvm;


#if _WINDOWS
#include <wtypes.h>
#include <WinBase.h>
#include <windef.h>
#include <WinUser.h>
#endif // _WINDOWS


FunctionPass *llvm::createMyPasses(bool flag)
{
      createMyPassPass(flag);
      return NULL;
}

extern "C"
{
      // 在clang里根据配置创建自定义pass,called by PassManagerBuilder::populateModulePassManager
      void __stdcall clangAddCustomPass(legacy::PassManagerBase &MPM)
      {
                MPM.add(createMyPassPass(true));
      }
}

MyCustom目录下创建CMakeLists.txt文件,内容如下:
cmake_minimum_required(VERSION 3.4)

project(custompass)

# 设置编译模式
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")      #/MT
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")                #/MTd

# 添加源码目录
aux_source_directory(./srcsrc_1)
set(srcs ${src_1})

# 生成动态链接库,那么在Windows下就是dll,最终会生成custompass.dll
add_library(custompass SHARED ${srcs})

# 添加头文件,需要编译后的以及源码头文件
include_directories("C:/llvm/llvm-6.0.0.build/include")
include_directories("C:/llvm/llvm-6.0.0.src/include")

# 添加编译后的lib
set(mylibdir "C:/llvm/llvm-6.0.0.build/Release/lib")
set(VTKLIBS LLVMCore LLVMSupport LLVMAnalysis LLVMBinaryFormat LLVMTransformUtils)
foreach(libname ${VTKLIBS})
      SET(FOUND_LIB "FOUND_LIB-NOTFOUND")
      find_library(FOUND_LIB NAMES ${libname} HINTS ${mylibdir} NO_DEFAULT_PATH)
                IF (FOUND_LIB)
                        message("found lib: ${FOUND_LIB}")
                        LIST(APPEND mylibs ${FOUND_LIB})
                ELSE()
                        MESSAGE("not lib found: ${libname}")
                ENDIF ()
endforeach(libname)
#message(${mylibs})

#message(${CPPUNIT_LIBRARY})
target_link_libraries(custompass PUBLIC ${mylibs})

至此目录结构如下:
MyCustom
      src
                MyCustom.cpp
                MyCustom.h
                MyPass.cpp
                MyPass.h
      CMakeLists.txt

之后进行cmake得到vs工程,编译生成custompass.dll

步骤2 让clang使用上所编译的dll
注意到原文中的方法,是去修改其llvm/lib/Transforms/IPO/PassManagerBuilder.cpp中的populateModulePassManager
然后去导出函数,进行调用
void PassManagerBuilder::populateModulePassManager(
    legacy::PassManagerBase &MPM)
{
      pfn_clangAddCustomPass pfn = (pfn_clangAddCustomPass)::GetProcAddress(hPassDll, "clangAddCustomPass");
      if (pfn != NULL)
      {
                pfn(MPM);
      }
      else
      {
                //not found custompass.dll or clangAddCustomPass
      }
}
这样做不是不行,但注意到如原文所说
1.还是编译慢
2.如果想多添加一些内容,这涉及到又要去修改populateModulePassManager(当然你可以全部放在clangAddCustomPass中)
还是不够理想,那么你可以考虑如下方案
那就是Hook
通过对dll的导出以及调用,很轻易的,我们可以联想到hook
看了看clang会加载version,那就好办了

首先修改populateModulePassManager,添加足够的nop提供给我们去hook

添加足够的nop,也可以让我们很容易的获取到rva

接下来就是去修改version,调用自己的dll了,由于比较懒直接在version进行操作
使用到的是校长的version_x86代码,添加如下:
char hex[] = { 0xff, 0x75, 0x08, 0xe8, 0x0, 0x0, 0x0, 0x0 };
int call_addr_offset = 4;
int rva_offset = 0xa;
HMODULE base = GetModuleHandle(NULL);
if (base == NULL)
{
      OutputDebugStringA("Base failed");
      return;
}
void* pfn = (void*)GetProcAddress(hModule, "_clangAddCustomPass@4");
if (pfn == NULL)
{
      OutputDebugStringA("GPA failed");
      return;
}
DWORD addr = 0x00F00DA0 - 0x00400000 + (DWORD)base + rva_offset;
DWORD old;
DWORD repaddr = (DWORD)pfn - addr - 5 - 3;
if (!VirtualProtect((LPVOID)(addr), 8, PAGE_EXECUTE_READWRITE, &old))
{
      OutputDebugStringA("VP failed");
      return;
}

memcpy( hex + call_addr_offset, &repaddr, 4);
memcpy( (void*)addr, (void*)hex, sizeof(hex) / sizeof(char));

注意到0x00F00DA0 - 0x00400000是函数populateModulePassManager的起始va

接着把clang.exe version.dll custompass.dll放在bin下面就有了如下效果


可以看到,我们可以去多申请空间,然后去hook,这样就很大程度避免了去编译那个llvm的庞然大物了
同时我们也可以继续优化自己的代码,这样就只修改那份clang,因为我们不能保证bin目录下的其他exe会去调用version

Bu弃 发表于 2018-5-20 18:06:30

沙发,,表哥威武

iCookiie 发表于 2019-4-12 06:57:23

Thanks's for share!

詁瀸徛 发表于 2019-5-10 07:09:10

大大原文挂了?..
页: [1]
查看完整版本: 对Windows下的LLVM之把pass抽离到DLL中的学习以及优化