搭建模板
在上文lldb的流程&环境的配置一文中已经分析过,lldb-plugin被调用到的关键点在于会在插件动态库的Mach-O中寻找lldb::PluginInitialize符号并以此为入口来进行调用。
因此,创建lldb-plugin的三要素如下:
- 在插件中实现
lldb::PluginInitialize - 自定义
AnalyzeAddressCommand继承自lldb::SBCommandPluginInterface - 重写
DoExecute方法
创建工程的步骤:
- 创建一个动态库
- 引入
lldb头文件 HEADER_SEARCH_PATHSLD_RUNPATH_SEARCH_PATHS=/Applications/Xcode.app/Contents/SharedFrameworksOTHER_LDFLAGS=$(inherited) -F "/Applications/Xcode.app/Contents/SharedFrameworks" -framework "LLDB"
根据上述步骤和所需元素来进行搭建工程即可,至此lldb-plugin模板搭建完成
制作插件
填充模板
现在就可以开始做一个插件了,功能是通过插件来判断输入的地址属于哪里,根据之前的模板来填充相关信息,如下图所示
提示:
AddMultiwordCommand用于多个关键字的命令,例如此处HD address 0x000001- 如果是
p或者po命令属于单个关键字命令,则使用AddCommand即可
创建测试工程
在对插件进行调试的时候一种方法是将动态库每次打包然后进入lldb环境进行调试,显然这种方法并灵活,而且无法进行实时的调试,那么这里新建一个单元测试来对插件进行调试。
创建好单元测试工程后需要与主工程进行关联
因为需要对插件进行测试,所以需要将一些API暴露在头文件,所以将之前的部分内容重新写入头文件中,然后在测试文件中进行引入,同时对于lldb动态库的引入也需要像之前一样在测试工程中引入
一切准备就绪后cmd+U开始测试发现并无报错即可
测试代码
在之前的文章中对lldb的流程进行了分析,所以现在如果对插件进行测试的话,应当如以下步骤:
- 初始化
SBDebugger - 设置环境为同步环境
- 创建一个
Target - 加载一个可执行文件并添加一个断点
- 验证插件
使用的可执行文件如下图,并且使用命令对其进行编译clang -g test.c
运行后发现正确命中打入断点的位置,测试工程搭建完毕。
对插件进行测试
在测试工程中对插件进行测试有两种方法,一种是手动来调用一种是使用plugin load命令
手动调用
手动调用很简单就是自己来添加命令并直接调用,运行后可以看到调用成功
plugin load
手动去加载一下插件即可
运行后可以看到插件加载成功且成功调用
判断地址插件的核心
判断地址无非是分为三个位置来判断:
MachO中- 栈上
- 堆上
例如现在加载的文件中可以很清楚的判断到:
m处于MachO中p处于堆上b处于栈上
是否在MachO中
那么现在工程中在创建了Target后将断点打在此文件的17行处,即可得到所有的变量,便可以使用命令HD address m的地址来查看其地址处于什么位置
判断传入的地址是否在MachO中的关键是判断这个地址是否有Section的信息,如果判断成立的话则进入专门判断MachO的方法中
其中是对MachO中信息的一些收集并整体返回出来,结果如下图
同样可以根据命令objdump --macho -d 可执行文件来查看到func1位于__TEXT,__text
是否在栈上
判断是否在栈上就更为简单了,只需要得到相应线程中的栈帧信息,从中便可以取得栈顶和栈底,那么只需要判断当前地址是否处于栈顶和栈底之间,即可判断出此地址是否在栈空间上
在测试代码中来验证b的地址是否位于栈上
是否位于堆空间
是否位于堆空间可以使用函数malloc_zone_from_ptr来判断,同时可以使用malloc_size来看到分配的内存空间有多大
打印结果为0x600000f145e0 heap pointer, (0x10 bytes), zone: 0x1d641c000,可以得知obj对象分配的内存空间位于堆空间,并且大小为16字节,那么通过objc的方式判断堆空间的代码已经有了,同样可以通过c++还有Swift来实现这段代码
c++
这段代码就是利用上面说过的函数来判断地址是否在堆空间,但是这里居然显示不在堆空间上,因为通过命令HD address 0x00001,这个地址应当在插件所在的进程中进行判断,而此时的判断则是发生在lldb所在的进程也就是插件执行的环境,而这个地址则需要在执行插件所在的调试环境来判断,这两个环境是分别有自己的环境,所以需要将这部分代码放在执行插件所在的调试环境。
swift
同理,在swift中的判断原理也是一样的,只不过代码不同
总结
以上就是制作以及调试lldb插件的步骤和流程,本次是使用c++来编写插件,使用单元测试来调试插件。