飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 5152|回复: 3

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

[复制链接]
  • TA的每日心情
    擦汗
    2016-4-19 21:35
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2018-5-20 10:02:09 | 显示全部楼层 |阅读模式
    本帖最后由 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:
    1. #ifndef __MYPASS_H
    2. #define __MYPASS_H


    3. // LLVM include
    4. #define DEBUG_TYPE "mypass"
    5. #include "llvm/IR/Function.h"
    6. #include "llvm/Pass.h"
    7. #include "llvm/Support/raw_ostream.h"
    8. #include "llvm/PassSupport.h"


    9. using namespace llvm;

    10. // Namespace
    11. namespace llvm
    12. {
    13.         FunctionPass *createMyPassPass(bool flag);
    14. }
    15. #endif __MYPASS_H
    复制代码



    MyPass.cpp:
    1. #include "MyPass.h"
    2. #include "llvm/IR/Function.h"
    3. #include "llvm/Pass.h"
    4. #include "llvm/IR/Function.h"
    5. #include "llvm/Support/raw_ostream.h"
    6. #include "llvm/InitializePasses.h"

    7. namespace
    8. {
    9.         struct MyPass : public FunctionPass
    10.         {
    11.                 static char ID;
    12.                 MyPass() : FunctionPass(ID) {}

    13.                 bool runOnFunction(Function &F)
    14.                 {
    15.                         errs() << "Hello,MyPass";
    16.                         errs().write_escaped(F.getName()) << "\n";
    17.                         return false;
    18.                 }
    19.         };
    20. }

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

    25. FunctionPass *llvm::createMyPassPass(bool flag)
    26. {
    27.         return new MyPass();
    28. }
    复制代码


    MyCustom.h:
    1. #ifndef __MY_CUSTOM_H
    2. #define __MY_CUSTOM_H

    3. #include "MyPass.h"

    4. namespace llvm
    5. {
    6.         // 在里面创建其他多个pass
    7.         FunctionPass *createMyPasses(bool flag);
    8. }

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

    14. #endif __MY_CUSTOM_H
    复制代码


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

    4. #include "llvm/InitializePasses.h"
    5. #include "llvm-c/Initialization.h"
    6. #include "llvm/InitializePasses.h"
    7. #include "llvm/PassRegistry.h"
    8. #include "llvm/IR/PassManager.h"
    9. #include "llvm/IR/LegacyPassManager.h"
    10. #include "llvm/IR/LegacyPassManagers.h"


    11. #include "MyCustom.h"

    12. using namespace llvm;


    13. #if _WINDOWS
    14. #include <wtypes.h>
    15. #include <WinBase.h>
    16. #include <windef.h>
    17. #include <WinUser.h>
    18. #endif // _WINDOWS


    19. FunctionPass *llvm::createMyPasses(bool flag)
    20. {
    21.         createMyPassPass(flag);
    22.         return NULL;
    23. }

    24. extern "C"
    25. {
    26.         // 在clang里根据配置创建自定义pass,called by PassManagerBuilder::populateModulePassManager
    27.         void __stdcall clangAddCustomPass(legacy::PassManagerBase &MPM)
    28.         {
    29.                 MPM.add(createMyPassPass(true));
    30.         }
    31. }
    复制代码


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

    2. project(custompass)

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

    6. # 添加源码目录
    7. aux_source_directory(./src  src_1)
    8. set(srcs ${src_1})

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

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

    14. # 添加编译后的lib
    15. set(mylibdir "C:/llvm/llvm-6.0.0.build/Release/lib")
    16. set(VTKLIBS LLVMCore LLVMSupport LLVMAnalysis LLVMBinaryFormat LLVMTransformUtils)
    17. foreach(libname ${VTKLIBS})
    18.         SET(FOUND_LIB "FOUND_LIB-NOTFOUND")
    19.         find_library(FOUND_LIB NAMES ${libname} HINTS ${mylibdir} NO_DEFAULT_PATH)
    20.                 IF (FOUND_LIB)
    21.                         message("found lib: ${FOUND_LIB}")
    22.                         LIST(APPEND mylibs ${FOUND_LIB})
    23.                 ELSE()
    24.                         MESSAGE("not lib found: ${libname}")
    25.                 ENDIF ()
    26. endforeach(libname)
    27. #message(${mylibs})

    28. #message(${CPPUNIT_LIBRARY})
    29. target_link_libraries(custompass PUBLIC ${mylibs})
    复制代码

    1. 至此目录结构如下:
    2. MyCustom
    3.         src
    4.                 MyCustom.cpp
    5.                 MyCustom.h
    6.                 MyPass.cpp
    7.                 MyPass.h
    8.         CMakeLists.txt
    复制代码


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

    步骤2 让clang使用上所编译的dll
    注意到原文中的方法,是去修改其llvm/lib/Transforms/IPO/PassManagerBuilder.cpp中的populateModulePassManager
    然后去导出函数,进行调用
    1. void PassManagerBuilder::populateModulePassManager(
    2.     legacy::PassManagerBase &MPM)
    3. {
    4.         pfn_clangAddCustomPass pfn = (pfn_clangAddCustomPass)::GetProcAddress(hPassDll, "clangAddCustomPass");
    5.         if (pfn != NULL)
    6.         {
    7.                 pfn(MPM);
    8.         }
    9.         else
    10.         {
    11.                 //not found custompass.dll or clangAddCustomPass
    12.         }
    13. }
    复制代码

    这样做不是不行,但注意到如原文所说
    1.还是编译慢
    2.如果想多添加一些内容,这涉及到又要去修改populateModulePassManager(当然你可以全部放在clangAddCustomPass中)
    还是不够理想,那么你可以考虑如下方案
    那就是Hook
    通过对dll的导出以及调用,很轻易的,我们可以联想到hook
    看了看clang会加载version,那就好办了
    2.png
    首先修改populateModulePassManager,添加足够的nop提供给我们去hook
    3.png
    添加足够的nop,也可以让我们很容易的获取到rva

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

    24. memcpy( hex + call_addr_offset, &repaddr, 4);
    25. memcpy( (void*)addr, (void*)hex, sizeof(hex) / sizeof(char));
    复制代码


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

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

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

    评分

    参与人数 2威望 +40 飘云币 +40 收起 理由
    tree_fly + 20 + 20 PYG有你更精彩!
    Bu弃 + 20 + 20 感谢发布原创作品,PYG有你更精彩!

    查看全部评分

    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2024-4-8 22:23
  • 签到天数: 110 天

    [LV.6]常住居民II

    发表于 2018-5-20 18:06:30 | 显示全部楼层
    沙发,,表哥威武
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2019-4-12 06:57:23 | 显示全部楼层
    Thanks's for share!
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2022-12-1 10:56
  • 签到天数: 136 天

    [LV.7]常住居民III

    发表于 2019-5-10 07:09:10 | 显示全部楼层
    大大  原文挂了?..
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表