对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 沙发,,表哥威武 Thanks's for share! 大大原文挂了?..
页:
[1]