This page looks best with JavaScript enabled

LLVM 调试环境配置

 ·  ☕ 5 min read · 👀... views

感谢@Richar,教我配出了超级方便的LLVM Pass源码级调试环境 Orzzzz

准备工作

  1. 按照前一篇博客 LLVM编译与FirstPass 配置好LLVM环境和环境变量,LLVM必须用Debug模式编译
  2. 安装好vscode 如果用的是虚拟机则配好vscode remote ssh,WSL的话配好vscode remote WSL

Vscode

本来我是想直接将调试环境配置到LLVM源码根目录。但是尝试之后发现用Remote的方式打开源码根目录,vscode要加载的东西太多,代码联想等功能都非常慢,体验很差。因为我们要调试的基本是自己写的Pass,后面发现可以直接配到自己Pass的目录下(这个目录可以在任意位置),然后把包含目录配置到vscode配置文件中就可以了。

现在,假定自己要开发的Pass目录名为MyPass,其中有Pass源代码文件 MyPass.cpp,目标源文件 target.cpp。在MyPass目录下,创建一个.vscode文件夹,在.vscode文件夹中建立三个文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// c_cpp_properties.json  用来配置要包含的头文件和宏
{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/mnt/c/Users/Qfrost/Desktop/code/LLVM/ollvm-12.x-main/llvm/include",       // 这里设置为LLVM源码目录的include目录
                "/mnt/c/Users/Qfrost/Desktop/code/LLVM/build/include"                       // 这里设置为LLVM_HOME的include目录
            ],
            "defines": [
                "_GNU_SOURCE",
                "__STDC_CONSTANT_MACROS",
                "__STDC_FORMAT_MACROS",
                "__STDC_LIMIT_MACROS"
            ],
            "compilerPath": "/mnt/c/Users/Qfrost/Desktop/code/LLVM/build/bin/clang",        // 这里设置为clang的绝对路径(vscode不加载环境变量,必须要用绝对路径)
            "cStandard": "c17",
            "cppStandard": "c++14",
            "intelliSenseMode": "linux-clang-x64",
            "compilerArgs": [
                "-fno-exceptions",
                "-fno-rtti"
            ]
        }
    ],
    "version": 4
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// launch.json   配置调试设置
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug Pass",
            "type": "cppdbg",
            "request": "launch",
            "program": "/mnt/c/Users/Qfrost/Desktop/code/LLVM/build/bin/opt",       // 这里设置为opt的绝对路径
            "args": [
                "-load",
                "${fileDirname}/build/${fileBasenameNoExtension}.so",
                "-${fileBasenameNoExtension}",
                "${fileDirname}/build/1-target_before.bc",
                "-o",
                "${fileDirname}/build/2-target_after.bc",
            ],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "sourceFileMap":{
                // "./": ""
            },
            "preLaunchTask": "build",
        }
    ]
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
// tasks.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",       // 设置build任务 即 make build -j2
            "type": "shell",
            "command": "make",     
            "args": [
                "build",
                "-j2",
            ],
            "options": {
                "cwd": "${workspaceFolder}",
                "env": {
                    "FILE": "${relativeFile}",
                    "FILE_DIR_NAME": "${fileDirname}",
                    "FILE_EXT_NAME": "${fileExtname}",
                    "FILE_BASENAME_NO_EXTENSION": "${fileBasenameNoExtension}"
                }
            },
            "isBackground": false,
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "dedicated",
                "showReuseMessage": true,
                "clear": true
            },
            "problemMatcher": {
                "owner": "cpp",
                "fileLocation": [
                    "absolute"
                ],
                "pattern": {
                    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            }
        },
        {
            "label": "run",     // 设置run任务
            "type": "shell",
            "command": "make",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "args": [
                "run",
                "-j2"
            ],
            "options": {
                "cwd": "${workspaceFolder}",
                "env": {
                    "FILE": "${relativeFile}",
                    "FILE_DIR_NAME": "${fileDirname}",
                    "FILE_EXT_NAME": "${fileExtname}",
                    "FILE_BASENAME_NO_EXTENSION": "${fileBasenameNoExtension}"
                }
            },
            "isBackground": false,
            "presentation": {
                "echo": true,
                "reveal": "always",
                "focus": false,
                "panel": "dedicated",
                "showReuseMessage": true,
                "clear": true
            },
            "problemMatcher": []
        },
        {
            "label": "clean",       // 设置clean任务
            "type": "shell",
            "command": "make",
            "args": [
                "clean"
            ],
            "options": {
                "cwd": "${workspaceFolder}",
                "env": {
                    "FILE": "${relativeFile}",
                    "FILE_DIR_NAME": "${fileDirname}",
                    "FILE_EXT_NAME": "${fileExtname}",
                    "FILE_BASENAME": "${fileBasename}"
                }
            },
            "isBackground": true,
            "presentation": {
                "reveal": "never",
            },
            "problemMatcher": []
        }
    ]
}

以上是.vscode目录下所有配置的内容。然后回到MyPass目录,需要再配置一个makefile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
TARGRT_FILE=./target.cpp		# 指定目标源文件名

cxxflags=`llvm-config --cxxflags`
ldflags=`llvm-config --ldflags`

build-pass:
	@bash -c "if [[ ! -d $(FILE_DIR_NAME)/build ]];then mkdir $(FILE_DIR_NAME)/build;fi" 	
	@g++ $(cxxflags) -ggdb -shared $(ldflags) -fPIC $(FILE) -o $(FILE_DIR_NAME)/build/$(FILE_BASENAME_NO_EXTENSION).so
	@echo "\033[32mBuild $(FILE) Successfully.\033[0m"

build-target:
	@bash -c "if [[ ! -d $(FILE_DIR_NAME)/build ]];then mkdir $(FILE_DIR_NAME)/build;fi"
	@clang -emit-llvm -c $(TARGRT_FILE) -o $(FILE_DIR_NAME)/build/1-target_before.bc
	@llvm-dis $(FILE_DIR_NAME)/build/1-target_before.bc
	@echo "\033[32mBuild target.cpp successfully.\033[0m"

clean:
	rm -rf ./build/*.{o,so,bc}

build: build-pass build-target

run: build
	@clear
	@opt -load $(FILE_DIR_NAME)/build/$(FILE_BASENAME_NO_EXTENSION).so -$(FILE_BASENAME_NO_EXTENSION) $(FILE_DIR_NAME)/build/1-target_before.bc -o $(FILE_DIR_NAME)/build/2-target_after.bc
	@llvm-dis $(FILE_DIR_NAME)/build/2-target_after.bc

大概就是配置这些文件。然后要使用的话,切记要用Remote模式的vscode。比如是WSL的,则要在WSL Shell中敲 “code .”来启动vscode,虚拟机的话则用vscode Remote SSH并打开对应文件夹。然后就可以按快捷键愉快build和调试自己的Pass了

  1. F5调试
  2. Ctrl+Shift+B Run
  3. 终端->运行任务 里面可以看到并运行配置的所有任务

LLVMPassDebug

Visual Studio

同样也是先按照LLVM 编译与First Pass配置好LLVM环境,根据cmake生成sln工程后在工程里编译一份。同样的也是建议用Debug模式编译,LLVM自身的编译优化等级很高,在Release或者RelWithDebInfo模式下编译挂源码调试,会出现很多局部变量被优化看不到值,纯靠猜。但是Debug模式编译已经很慢了,再加上VisualStudio的调试模式,慢+卡,非常的折磨

如果是单cpp文件的编译调试非常简单,不讲了,这边要介绍的是当有一个A.sln工程要用clang编译的时候,我们怎么完美复现环境并用vs调试clang

用VisualStudio来调试比vscode要方便很多,因为环境不用自己配了。我们可以先用LLVM报错排查技巧 -### 方法拿到A.sln工程底层传递到clang编译时的命令,比如

Build started...
1>------ Build started: Project: test, Configuration: Release x64 ------
1>clang version 19.0.0git QVMProtect	Release
1>Target: x86_64-pc-windows-msvc
1>Thread model: posix
1>InstalledDir: D:\LLVM\WindowsRelWithDebInfo\bin
1> (in-process)
1> "D:\\LLVM\\WindowsRelWithDebInfo\\bin\\clang-cl.exe" "-cc1" "-triple" "x86_64-pc-windows-msvc19.29.30151" "-emit-obj" "-mrelax-all" "-mincremental-linker-compatible" "-disable-free" "-clear-ast-before-backend" "-disable-llvm-verifier" "-discard-value-names" "-main-file-name" "test.cpp" "-mrelocation-model" "pic" "-pic-level" "2" "-mframe-pointer=none" "-relaxed-aliasing" "-fmath-errno" "-ffp-contract=on" "-fno-rounding-math" "-mconstructor-aliases" "-fms-volatile" "-funwind-tables=2" "-target-cpu" "x86-64" "-target-feature" "+avx" "-target-feature" "+avx2" "-mllvm" "-x86-asm-syntax=intel" "-tune-cpu" "generic" "-D_MT" "-flto-visibility-public-std" "--dependent-lib=libcmt" "--dependent-lib=oldnames" "-stack-protector" "2" "-fcxx-exceptions" "-fexceptions" "-fexternc-nounwind" "-fdefault-calling-conv=cdecl" "-fdiagnostics-format" "msvc" "-gno-column-info" "-gcodeview" "-debug-info-kind=constructor" "-fdebug-compilation-dir=E:\\code\\test" "-object-file-name=E:\\code\\test\\x64\\Release\\test.obj" "-ffunction-sections" "-fcoverage-compilation-dir=E:\\code\\test" "-resource-dir" "D:\\LLVM\\WindowsRelWithDebInfo\\lib\\clang\\19" "-D" "NDEBUG" "-D" "_CONSOLE" "-D" "_UNICODE" "-D" "UNICODE" "-internal-isystem" "D:\\LLVM\\WindowsRelWithDebInfo\\lib\\clang\\19\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\atlmfc\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Auxiliary\\VS\\include" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\ucrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\um" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\shared" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\winrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\cppwinrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\Include\\um" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\atlmfc\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Auxiliary\\VS\\include" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\ucrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\um" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\shared" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\winrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\cppwinrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\Include\\um" "-O0" "-Wall" "-Wno-error" "-Wsystem-headers" "-fdeprecated-macro" "-ferror-limit" "19" "-fno-use-cxa-atexit" "-fms-extensions" "-fms-compatibility" "-fms-compatibility-version=19.29.30151" "-std=c++20" "-fno-implicit-modules" "-fskip-odr-check-in-gmf" "-fno-caret-diagnostics" "-faddrsig" "-o" "x64\\Release\\test.obj" "-x" "c++" "test.cpp"

然后在LLVM的工程中,找到clang项目,配置clang项目的属性,clang Project -> Properties -> Configuration Properties -> Debugging

  1. Command:$(TargetPath),也就是clang.exe
  2. Command Arguments:把clang-cl.exe后面的参数直接全部复制进去
  3. Working Directory: 这里填A.sln工程的路径

clang_debug_properties

完事后,打上断点,在clang工程上右键,Debug -> Start New Instance启动调试。当然,用这种方法的调试,是不可以调试外部Pass模块的,比如你将pass编译成了一个.dll然后用接口或者任意的LoadLibrary等方式拉起加载,然后对着外部模块的源码断点是无法命中的,即使外部模块的项目挂在LLVM工程下也是不行的

Share on

Qfrost
WRITTEN BY
Qfrost
CTFer, Anti-Cheater, LLVM Committer