llvm学习(十五):lldb脚本开发基础

从零开始的 lldb 脚本开发。系列第一篇,讲环境搭建和 demo,优雅地开发 lldb 脚本。

编译和安装(非常详细)

作为 llvm 的忠实拥护者,当然要坚持拥护 lldb,但由于它上面实在是没什么插件,只能自己动手丰衣足食。

apt 简易版(限ubuntu)

sudo apt install lldb

或者

sudo apt install lldb-10 python3-lldb-10

根据依赖关系,lldb 这个包会自动定位到某个比较稳定的版本上,例如我的 ubuntu 20 在2021年09月05日,在安装时会安装一堆 llvm10 相关的东西(包括 python3-lldb-10)

1202年了,求求各位别再用 python2 了,也别再用 llvm6 了。

从源码编译

官方资料:https://lldb.llvm.org/resources/build.html , 非常靠谱。

1202年了,编译 llvm 越来越方便了,简要记录:

  1. https://github.com/llvm/llvm-project/releases 随便下载一份 llvm-project-12.0.0.src.tar.xz,解压。

  2. 执行命令,仅编译lldb和clang(官方说 lldb 依赖 clang),开启对 python 的支持 cmake ../llvm-project-12.0.0/llvm -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS='clang;lldb' -DLLDB_ENABLE_PYTHON=1

    这里我测试了一下,我本来想要 py3 结果编出了 py2,改了 PATH 后变成 py3 了,好像是和编译 python 这个命令本身的版本有关,编译时就确认好了,编译结束后无法修改解释器,不大确定细节。

仅编译 lldb

参考 https://lldb.llvm.org/resources/build.html#standalone-builds

假设你拥有了编译好了 llvm+clang 工程,可以让 lldb 使用这个 build 目录或者 release 目录,不需要从头再来,省时省力。

我使用的命令是:(好像这个会自动把 python 插件也编译了)

1
2
3
mkdir lldb-build
cd lldb-build
cmake -DLLVM_DIR=/path/to/build-12/lib/cmake ../llvm-project-12.0.0/lldb

或者

1
2
3
mkdir lldb-build
cd lldb-build
cmake -DLLVM_DIR=/path/to/release-12/lib/cmake ../llvm-project-12.0.0/lldb

MacOS存在的问题

mac上的 debugger 需要是被签名过的文件,并且该签名被系统信任,所以自己编译出来的 lldb 是损坏的,整得挺麻烦的,爱折腾的自己去官方翻,我是不爱折腾这个东西。

仅生成 python 接口文件(无需编译 c 代码)

上文描述的方式,会得到一个 lldb/__init__.py 文件,但它不大好用,可能是出于对 py2 兼容的考虑,函数的参数和返回值都没有数据类型,用起来还是不够爽,经过一番探索,找到了 python3 的支持方式。

lldb python 拓展的编译过程,通过阅读 cmake 文件可以略知端倪。

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

lldb/CMakeLists.txt

45 if (LLDB_ENABLE_PYTHON OR LLDB_ENABLE_LUA)
46 add_subdirectory(bindings)
47 endif ()

lldb/bindings/CMakeLists.txt
24 set(SWIG_COMMON_FLAGS
25 -c++
26 -features autodoc
27 -I${LLDB_SOURCE_DIR}/include
28 -I${CMAKE_CURRENT_SOURCE_DIR}
29 -D__STDC_LIMIT_MACROS
30 -D__STDC_CONSTANT_MACROS
31 ${DARWIN_EXTRAS}
32 )
34 if (LLDB_ENABLE_PYTHON)
35 add_subdirectory(python)
36 endif()

lldb/bindings/CMakeLists.txt

1 add_custom_command(
2 OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp
3 OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lldb.py
4 DEPENDS ${SWIG_SOURCES}
5 DEPENDS ${SWIG_INTERFACES}
6 DEPENDS ${SWIG_HEADERS}
7 COMMAND ${SWIG_EXECUTABLE}
8 ${SWIG_COMMON_FLAGS}
9 -I${CMAKE_CURRENT_SOURCE_DIR}
10 -c++
11 -shadow
12 -python
13 -threads
14 -outdir ${CMAKE_CURRENT_BINARY_DIR}
15 -o ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp
16 ${CMAKE_CURRENT_SOURCE_DIR}/python.swig
17 VERBATIM
18 COMMENT "Building LLDB Python wrapper")

经过阅读,根据是否开启 python 来确定下文执行内容,最终会调用到 swig 这个工具,并且有大量的命令行参数,生成 python 接口文件。

lldb 的 python 其实是调用底层 cpp 的实现,使用 swig 这个工具描述接口并且生成 API 文件。

swig 在生成 python 时有额外选项,参考下方 help:

1
2
swig -python --help
-py3 - Generate code with Python 3 specific features and syntax

而包括 llvm13 在内的全部版本,都是没有主动带有 py3 这个参数的,因此生成的文件没有 py3 的参数类型和函数类型。

官方在 https://github.com/llvm/llvm-project/commit/e5228ef556e5a4cb40c6c7936aada636e1b7be10 该次 commit 中将 py3 加入了命令行参数,并且在 llvm14 中落地。

为了解决手头的问题,我们可以选择手动执行这句 swig 来生成文件,经过尝试后,如下:

1
2
cd /tmp/lldb/bindings/python
swig -c++ -features autodoc -I/tmp/lldb/include -I/tmp/lldb/bindings -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -shadow -python -threads python.swig

会在 python.swig 的同目录下生成 lldb.py,就是我们要找的文件了。

然后在里面加上 -py3 这个选项,就可以生成 py3 的文件了。

简单测试

lldb 和 gdb 的命令和用法差不多,绝大部分情况下都能正确执行插件。

输入 script 可以进入交互模式,import lldb已经被执行过了,可以直接访问 lldb.debugger 这种对象。

输入 script print("Hello") 可以直接执行 python。

反面教材:

从源码编译时,如果忘了开 LLDB_ENABLE_PYTHON,这里 script 就会弹一句什么报错,暗示没有支持的脚本语言,需要重新编译。

为IDE提供代码补全索引

老生常谈,本人作为没了 IDE 就活不下去的开发者,一定要做到类型推断和代码补全。(这也是我常年诟病 py2 的原因)

无论是从 apt 安装,还是从源码编译,在目录里都可以翻到这个 lldb/__init__.py 文件,附近还必有一个 lldb.so 文件:

输入 `script print(lldb)` 能找到这个 module。
例如:
1
2
<module 'lldb' from '/usr/lib/python3/dist-packages/lldb/__init__.py'>
<module 'lldb' from '/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Resources/Python/lldb/__init__.py'>

或者直接生成 lldb.py,参考上文描述的内容。

lldb/__init__.py 复制粘贴(或者软连接)到第三方库的路径下,相当于:

1
ln -s /Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Resources/Python/lldb /path/to/lldb
1
2
mkdir /path/to/lldb
cp __init__.py /path/to/lldb/

调用 import lldb 后,相关的数据结构就都有了,注释很多,省的到官网去查了。

以官方的 demo 为例,成功后如图:

1
2
3
def __lldb_init_module(debugger, internal_dict):
pass
# Command Initialization code goes here

好,终于可以动手写代码了!

结语

预告:下一篇介绍 lldb 模块提供的 API,介绍插件的整体设计。