美文网首页
LLDB调试(二)

LLDB调试(二)

作者: 浅墨入画 | 来源:发表于2021-03-04 23:55 被阅读0次

    前言:

    lldb断点类型:

    1. 软件断点:正在调试的二进制文件中的断点,在调试器应使用一种迫使 CPU发出软件中断(又称为trap)的操作码来 停止我们的程序的位置替 换现有的操作码,击中断点并发送中断后,调试器将接收中断信号,将操 作码替换为原始操作码,等待下一个命令。
    2. 硬件断点:使用专用硬件,例如断点的x86调试寄存器,观察CPU和停止执 行程序的当前状态。
    3. 非符号断点:如果标记了一行代码以供调试器停止程序执行,代码中的位 置会转换为调试器实际在其中放置断点的执行地址。
    4. 符号断点:直接绑定到符号。

    一. lldb复制器

    lldb Reproducer :为了解决复现在使用lldb过程中遇到的bug,会在 lldb运行过程中捕获所有必要的信息,方便以后在调试调试器时重播调试会话。
    lldb Reproducer 有两个功能:

    1. 捕捉
    2. 重播
      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两种方式:

    1. 系统定义的Plugin:LLDB.framework/Resources/PlugIns
    2. 用户自定义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++函数使用:

    1. SBDebugger:调试器对象,lldb.SBDebugger对象拥有在调试会话的命令
      解释器和所有目标。
    2. SBTarget:当前选定的目标,lldb.SBTarget管理一个正在运行的进程, 以及该进程的使用的可执行文件和调试文件。
    3. SBProcess:当前选定的target所在的process,lldb.SBProcess对象 管理线程并控制访问存储的过程。
    4. SBThread:当前选定的线程,lldb.SBThread对象管理该线程的堆栈帧。
    5. SBFrame:当前选定的堆栈frame,该lldb.SBFrame对象管理当前堆栈信息 和该堆栈使用到的寄存器组。

    加载lldbinit文件两种方式:

    1. 全局生效.lldbinit文件:将.lldbinit文件放置在根目录~/下
    2. 指定项目生效:手动设置的.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

    手动在Xcode配置.lldbinit文件如下 image.png
    (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++函数使用:

    1. SBCommandInterpreter:用于处理/解释lldb的命令。
    2. SBBreakpoint:创建和管理断点。

    SBCommandInterpreter: 处理解析,添加,执行
    CommandObject :DoExecute:处理命令的地方

    操作:手动创建包装一个可执行文件,现在通过api给可执行文件打断点
    Cmd + Shift + N ,选择如下模版,创建工程 SBAPI学习

    image.png
    // 显示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`是否被关闭。
    

    相关文章

      网友评论

          本文标题:LLDB调试(二)

          本文链接:https://www.haomeiwen.com/subject/ofkhqltx.html