llvm学习(十四):以 llvm-readelf 为例,源码级调试LLVM子项目

本文介绍如何在 CLion 里调试 llvm-readelf,总体思路是建立合适的 cmake 项目,使用合适的IDE 进行调试即可。

一、背景

环境:MacOS,CLion,自行编译完成的LLVM环境(参考 LLVM 第一篇)。

本来是想看看 readelf 源码来学习 ELF 文件格式,但由于肉眼实在是看不懂,就准备下断点调试。llvm-readelf 是软连接到 llvm-readobj 的,源码位于 llvm-project-10.0.0/llvm/tools/llvm-readobj ,而且刚好是一个叶子节点的 Cmake 项目,就尝试将其放到 CLion 里,独立出来,借助LLVM_HOME 目录下的静态链接库和头文件运行。

二、操作步骤

1、编写cmake 文件,使用 llvm 提供的cmake 环境

把原先的 cmake 文件删掉,写成下面的格式(照抄 llvm pass 的编写即可)

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
cmake_minimum_required(VERSION 3.4)
project(llvm-readobj)

# we need LLVM_HOME in order not automatically set LLVM_DIR
if (NOT DEFINED ENV{LLVM_HOME})
message(FATAL_ERROR "$LLVM_HOME is not defined")
else ()
set(ENV{LLVM_DIR} $ENV{LLVM_HOME}/lib/cmake/llvm)
endif ()

find_package(LLVM REQUIRED CONFIG)
add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
if (${LLVM_VERSION_MAJOR} VERSION_GREATER_EQUAL 10)
set(CMAKE_CXX_STANDARD 14)
endif ()

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk")

add_executable(llvm-readelf
ARMWinEHPrinter.cpp
COFFDumper.cpp
COFFImportDumper.cpp
ELFDumper.cpp
Error.cpp
llvm-readobj.cpp
MachODumper.cpp
ObjDumper.cpp
WasmDumper.cpp
Win64EHDumper.cpp
WindowsResourceDumper.cpp
XCOFFDumper.cpp
)

set_target_properties(llvm-readelf PROPERTIES
COMPILE_FLAGS "-fno-rtti"
)

target_link_libraries(llvm-readelf
// TODO: import static library
)

这时候就能使用CLion 打开它,有高亮补全,点击编译,会提醒缺失一大堆符号,这是我们预期之内的。

在 LLVM_HOME/lib/cmake/llvm 的 LLVMExports.cmake 文件里,存放了一系列可以被连接的静态库,为了验证方案可行性,先把 target_link_libraries 的内容写全了,每个文件都写进去(太长我就不贴了)。

这时候,llvm-readelf 可以正常运行了。

2、二分法移除不必要的依赖

target_link_libraries 里不是每一项都会被用到,为了优雅,就需要移除不必要的依赖,可以参考 LLVMBuild.txt 的内容。

1
2
3
4
5
[component_0]
type = Tool
name = llvm-readobj
parent = Tools
required_libraries = all-targets BitReader Object BinaryFormat DebugInfoCodeView DebugInfoPDB DebugInfoMSF

经过测试,最后得到最小的集合是:

1
2
3
target_link_libraries(llvm-readelf
LLVMDebugInfoDWARF
)

于是准备工作就完成了

3、直接进行调试

使用 CLion 运行,修改命令行参数,下断点,即可正常调试。

三、效果

非常优雅!

四、总结

虽然 LLVM 非常庞大,使用 cmake 做模块管理,模块化做的还是很好的,舒服了。