🌚

Kam's Online Notebook


在 lldb 中命中函数断点后自动输出返回值

怎么在 lldb 中命中设置的函数断点后自动输出返回值?原本设想的方式是在断点的 command 中加入 finish(thread step-out) 和 po 命令实现,然后在实际执行的时候触发 error: Aborting reading of commands after command #1: 'finish' continued the target. 错误。

怎么回事

finish其实是thread step-out的缩写,从 lldb 的帮助文档,可以看到这条指令的作用是

Finish executing the current stack frame and stop after returning
Defaults to current thread unless specified.

在 IDE 上触发断点,我们确实可以执行finish去到函数返回的地方,然后通过po $x0输出返回值。但是其实这已经是「另一个断点」了。这意味着,给上一个断点添加的 command 无法在这个断点上执行,所有会出现开篇提到的错误。

可以怎么做

模仿finish的行为——完成当前函数调用并停止在返回点。「完成当前函数调用」这个很简单,直接continue让程序继续执行就 vans 了。「停止在返回点」这个需要在返回地址设置断点,根据「 bl/ble 指令调用时会将返回地址写到 link regiseter($lr)中去」这点,可以尝试在触发函数断点 A 时,获取返回地址,再设置一个返回地址的断点 B。

(lldb) br set -n "-[ViewController switchAction:]" -K false
(lldb) br command add
Enter your debugger command(s).  Type 'DONE' to end.
> br set -a $lr -o true -G true -C "po $x0"
> continue
> DONE

简单梳理下:第一行设置我们想观察的 breakpoint,注意这里添加了 -K false 的参数

-K ( –skip-prologue )
sKip the prologue if the breakpoint is at the beginning of a function. If not set the
target.skip-prologue setting is used.

使得触发断点的位置在这段子程序的最前面,不要跳过函数的 prologue 部分,因为跳过的话大概率已经把返回地址压入栈了,然后用 link register 装了其他奇奇怪怪的东西,那么定位返回地址的任务会比较复杂。触发断点 A 后执行的br set -a $lr -o true -G true -C "po $x0"设置断点 B 以及附属指令。其中:

  1. -a 指定$lr寄存器的值设置断点;
  2. -o 设置为 one-shot 断点,触发一次就删除,这是因为每次触发断点 A 都要创建断点 B;
  3. -G 是 auto-continue 配置,触发后执行 command 自动 continue(说起来 A 也可以 auto-continue 🙂);
  4. -C 是设置断点 B 的 command。

权当抛砖引玉,不做更多的优化和定制,用处没有很大。

EOF

— Aug 5, 2020