前言:
lldb断点类型:
- 软件断点:正在调试的二进制文件中的断点,在调试器应使用一种迫使 CPU发出软件中断(又称为trap)的操作码来 停止我们的程序的位置替 换现有的操作码,击中断点并发送中断后,调试器将接收中断信号,将操 作码替换为原始操作码,等待下一个命令。
- 硬件断点:使用专用硬件,例如断点的x86调试寄存器,观察CPU和停止执 行程序的当前状态。
- 非符号断点:如果标记了一行代码以供调试器停止程序执行,代码中的位 置会转换为调试器实际在其中放置断点的执行地址。
- 符号断点:直接绑定到符号。
一. lldb复制器
lldb Reproducer :为了解决复现在使用lldb过程中遇到的bug,会在 lldb运行过程中捕获所有必要的信息,方便以后在调试调试器时重播调试会话。
lldb Reproducer 有两个功能:
- 捕捉
- 重播
lldb Reproducer 在捕捉时,会将调试过程中的所有信息包括调试的对象 信息,例如使用的符号、Mach-o的地址,调试信息路径,进程信息,内存信息保存下来,以便下次重播。
// 保存这次调试的过程,使用捕获capture
$ lldb --capture
(lldb) file test
Current executable set to '/Users/wangning/Documents/资料/3:1/第十二节课、LLDB深入学习(中)/上课代码/01-Reproducer/test' (x86_64).
// 添加符号断点 b main,所有符号断点都被lldb转换成地址0x0000000100003f70
(lldb) b main
Breakpoint 1: where = test`main, address = 0x0000000100003f70
(lldb) r
Process 15930 launched: '/Users/wangning/Documents/资料/3:1/第十二节课、LLDB深入学习(中)/上课代码/01-Reproducer/test' (x86_64)
Process 15930 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100003f70 test`main
test`main:
-> 0x100003f70 <+0>: pushq %rbp
0x100003f71 <+1>: movq %rsp, %rbp
0x100003f74 <+4>: subq $0x10, %rsp
0x100003f78 <+8>: movl $0x0, -0x4(%rbp)
Target 0: (test) stopped.
// 显示当前汇编
(lldb) di -f
test`main:
-> 0x100003f70 <+0>: pushq %rbp
0x100003f71 <+1>: movq %rsp, %rbp
0x100003f74 <+4>: subq $0x10, %rsp
0x100003f78 <+8>: movl $0x0, -0x4(%rbp)
// 同时显示当前汇编 以及 源码
(lldb) di -f -m
// 同时显示当前汇编 源码 操作码
(lldb) di -f -m -b
// 查看当前reproducer状态,当前处于capture mode,并且把捕捉信息存放到路径 /var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b 下
(lldb) reproducer status
Reproducer is in capture mode.
Path: /var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b
// 结束捕捉模式,并且把所有调试信息存放到路径/var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b下
(lldb) reproducer generate
Reproducer written to '/var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b'
Please have a look at the directory to assess if you're willing to share the contained information.
// 恢复上面所有操作命令,上面所有操作命令都会在终端展示出来
$ lldb --replay /var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b
二. lldb插件入口
lldb调试进程信号量类型
- SIGINT: interrupt
- SIGPIPE: 告诉进程连接已经断开,不要再操作
- SIGWINCH: 窗口大小改变
- SIGTSTP: 停止信号,终端发出(
Ctrl-Z
) - SIGCONT: 恢复停止的进程
提前加载plugin两种方式:
- 系统定义的
Plugin
:LLDB.framework/Resources/PlugIns - 用户自定义
Plugin
:~/Library/Application Support/LLDB/ PlugIns
操作:用户自定义方式加载插件
// 进入自定义目录,注意空格转义
$ cd ~/Library/Application\ Support
如果Application Support目录下没有LLDB文件夹就手动创建,并且在LLDB文件夹下创建PlugIns文件夹,并把插件libfooplugin.dylib放入,如下图所示
image.png
// 创建空工程,ViewController.m方法viewDidLoad中打断点,进入lldb
// 可以使用plugin load方式把插件加入到 这一次调试周期内,也可以把插件放入系统指定的路径下,插件对全局都能生效
(lldb) plugin load
三. lldb源码与API学习
- 最上面调试器包装成SBDebugger
- 命令解释器SBCommandInterpreter
- 最后所有命令都会被包装成CommandObject
LLDB API可以通过脚本桥接接口作为Python函数使用或者C++函数使用:
- SBDebugger:调试器对象,lldb.SBDebugger对象拥有在调试会话的命令
解释器和所有目标。 - SBTarget:当前选定的目标,lldb.SBTarget管理一个正在运行的进程, 以及该进程的使用的可执行文件和调试文件。
- SBProcess:当前选定的target所在的process,lldb.SBProcess对象 管理线程并控制访问存储的过程。
- SBThread:当前选定的线程,lldb.SBThread对象管理该线程的堆栈帧。
- SBFrame:当前选定的堆栈frame,该lldb.SBFrame对象管理当前堆栈信息 和该堆栈使用到的寄存器组。
加载lldbinit文件两种方式:
- 全局生效.lldbinit文件:将.lldbinit文件放置在根目录~/下
- 指定项目生效:手动设置的
.lldbinit
文件
创建.lldbinit文件,内容如下
command script import ./lldbinit.py
$ lldb --local-lldbinit
(lldb) file test
Current executable set to '/Users/wangning/Documents/资料/3:1/第十二节课、LLDB深入学习(中)/上课代码/02-lldb与.lldbinit/test' (x86_64).
// lldb可以预先内置一些命令
(lldb) help lldbinitcmds
error: 'lldbinitcmds' is not a known command.
Try 'help' to see a current list of commands.
Try 'apropos lldbinitcmds' for a list of related commands.
Try 'type lookup lldbinitcmds' for information on types, methods, functions, modules, etc.
(lldb) command script add -f lldbinit.cmd_lldbinitcmds lldbinitcmds
(lldb) help lldbinitcmds
For more information run 'help lldbinitcmds' Expects 'raw' input (see 'help
raw-input'.)
Syntax: lldbinitcmds
Function lldbinit.cmd_lldbinitcmds was not found. Containing module might be missing.
(lldb) lldbinitcmds
// 命令expression -O -- 起别名为Cat
(lldb) command alias Cat expression -O --
(lldb) Cat 123
123
修改.lldbinit文件,内容如下
command script import ./lldbinit.py
command alias Cat expression -O 1%
// 1%表示输入第一个内容, 2%表示第二个
创建lldbinit.text, 内容为command alias Cat expression -O %1
(lldb) help Cat
Evaluate an expression on the current thread. Displays any returned value
with LLDB's default formatting. Expects 'raw' input (see 'help
raw-input'.)
Syntax: Cat <cmd-options> -- <expr>
...
REPL
全称为Read-Eval-Print Loop
(“读取-求值-输出”循环), 一个简单 的,交互式的编程环境,用来接收代码的输入并直接输出回应。
LLDB API可以通过脚本桥接接口作为Python函数使用或者C++函数使用:
- SBCommandInterpreter:用于处理/解释lldb的命令。
- SBBreakpoint:创建和管理断点。
SBCommandInterpreter: 处理解析,添加,执行
CommandObject :DoExecute:处理命令的地方
操作:手动创建包装一个可执行文件,现在通过api给可执行文件打断点
Cmd + Shift + N ,选择如下模版,创建工程 SBAPI学习
// 显示Xcode内置的python动态库所在位置
$ lldb -P
/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python3
$ open /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python3
打开上面Python3文件,可以看到有LLDB动态库,如下图所示
image.png
1. 接下来把lldb所有头文件拷贝到工程 SBAPI学习 目录下
2.为了方便把lldb头文件导入 Header Search Paths,创建Config.xcconfig文件进行配置
// Config.xcconfig文件配置如下
HEADER_SEARCH_PATHS = ${SRCROOT}/include
LD_RUNPATH_SEARCH_PATHS = /Applications/Xcode.app/Contents/SharedFrameworks
image.png
// main.mm文件内容
#import <Foundation/Foundation.h>
#import <lldb/API/LLDB.h>
using lldb::SBDebugger;
int main(int argc, const char * argv[]) {
//初始化配置
SBDebugger::Initialize();
//初始化SBDebugger
SBDebugger debugger = SBDebugger::Create();
NSLog(@"%d", debugger.GetNumTargets());
return 0;
}
// 正常打印 SBAPI学习[8196:430497] 0
// 现在想边写代码边调试
// 替换上面动态库LLDB为liblldb.12.0.0git.dylib(llvm中获取)
// Config.xcconfig文件内容修改如下
HEADER_SEARCH_PATHS = ${SRCROOT}/include
LD_RUNPATH_SEARCH_PATHS = ${SRCROOT}
(lldb) b SBDebugger::Create
Breakpoint 2: 3 locations
// 执行断点,跳入lldb源码
阻止自省和调试尝试的功能是系统完整性保护,也称为无根。该系统限制了程序可以执行的操作(即使它们具有root访问权限),以阻止恶意软件将其自身植入系统内部。
1、重启`macOS`
2、不停的按`Command + R`,直到`Apple logo`出现。进入`Recovery Mode`。
3、选择`Terminal`。输入:
```bash
csrutil disable && reboot
```
4、重启,然后打开终端,输入:
```bash
csrutil status
```
查看当前`Rootless`是否被关闭。
网友评论