如果输出数据仅在返回C函数时可用,如何使用LLDB自动捕获该数据?
我编译的二进制代码没有任何源代码,但我知道它包含一个带有以下签名的C函数如果输出数据仅在返回C函数时可用,如何使用LLDB自动捕获该数据?,c,debugging,lldb,C,Debugging,Lldb,我编译的二进制代码没有任何源代码,但我知道它包含一个带有以下签名的C函数 void generateMoreData ( char * destination, long size ) 该函数的符号在LLDB调试器中可见,我希望捕获所有生成的数据 目前我知道我可以通过以下方式捕获数据: 我设置了一个断点断点集-n generateMoreData 一旦到达断点,我将检查$rdi和$rsi的值,因为System V的x86_64 ABI(由Linux、BSD、macOS和Solaris使用)在这
void generateMoreData ( char * destination, long size )
该函数的符号在LLDB调试器中可见,我希望捕获所有生成的数据
目前我知道我可以通过以下方式捕获数据:
断点集-n generateMoreData
$rdi
和$rsi
的值,因为System V的x86_64 ABI(由Linux、BSD、macOS和Solaris使用)在这些寄存器中传递前两个参数线程步出返回
x-c COUNT ADDRESS
转储数据,其中COUNT
是$rsi
的值,ADDRESS
是$rdi
的值,如步骤(2)所示- 问题A:只有在输入函数时,
和$rdi
才包含我需要的值,但当函数返回时,这些寄存器被函数使用,因此丢失了它们的初始值,这些值就不再存在了$rsi
- 问题B:我可以使用
设置在命中断点时要执行的命令,但这些命令不能包含break command add
,因为此命令将继续执行,而继续执行的第一个命令将停止处理断点命令,因此,在此命令之后设置的任何命令都不会执行线程跳出
- 该问题的总体解决方案是实际使用两个断点
首先,我们通过将所需值存储到LLDB变量来解决问题A:
break set -G true -n generateMoreData
break command add 1
> expr long $destination = $rsi
> expr long $size = $rdi
> DONE
-G true
确保程序在执行所有命令后自动继续,并且使用expr
可以将寄存器内容存储到我们命名为$destination
和$size
的变量中
为了解决问题B,我们需要在generateMoreData
的返回指令处设置另一个断点。如果generateMoreData
有多条返回指令,我们需要在每条返回指令上都有一个断点,但我们假设这是一个相当简单的函数,通常只有一个断点
首先,我们需要进入该函数,所以我们只需在其上设置一个普通断点,然后让调试器点击它。然后我们可以使用dis
反汇编函数。输出可能类似于下面的输出:
-> 0x7fff76be0bac <+0>: pushq %rbp
0x7fff76be0bad <+1>: movq %rsp, %rbp
0x7fff76be0bb0 <+4>: pushq %r14
0x7fff76be0bb2 <+6>: pushq %rbx
0x7fff76be0bb3 <+7>: subq $0x40, %rsp
:
0x7fff76be0c5f <+179>: popq %r14
0x7fff76be0c61 <+181>: popq %rbp
0x7fff76be0c62 <+182>: retq
0x7fff76be0c63 <+183>: nop
-R
以字节为单位设置相对偏移量。捕获的数据将写入generateMoreData.txt
以供以后检查(-o
设置输出文件,并且--append outfile
确保追加新数据而不是覆盖现有数据)
现在只需运行程序,最后可以检查输出文件中生成的所有数据。计算步骤B断点地址的更简单方法是利用父帧中的pc值始终是该帧的返回pc这一事实。所以我们可以让断点1的命令找出它。由于在到达第二个断点时没有使用来自当前帧的任何信息,因此您不在乎是在返回时停止还是在返回后停止 您必须记住首先清除旧的断点,但在lldb中使用命名断点很容易做到:
break set -G true -n generateMoreData --skip-prologue false
breakpoint name configure SecondBreakpoint -G true -C "x -o generateMoreData.txt --append-outfile -c \`$size\` $destination" -C "break delete SecondBreakpoint"
break command add 1
> expr long $destination = $arg1
> expr long $size = $arg2
> up
> break set -N SecondBreakpoint -a $pc
> DONE
注意,我们不必将命令添加到我们创建的断点,因为我将它添加到了名称中,所以新断点将从该名称继承它们
我还使用了$arg1
和$arg2
而不是$rsi
和$rdi
。这只是一个方便的lldb别名,以防您记不起哪个是哪个
此外,如果可能从多个线程调用此函数,则解决此问题会变得更加困难。然后您需要获取当前线程并设置一个特定于线程的断点。此外,如果要递归调用,则必须为每个返回帧生成不同的$size和$destination值
如果您开始尝试处理这类问题,那么最好对断点使用Python回调。然后,不必像Mecki聪明的解决方案那样存储返回时要打印的数据,您可以维护一个小Python数据结构,它可以记住每个帧/线程组合的$size和$destination,并在每次调用返回时正确打印。非常有趣的解决方案。在一个断点内设置一个断点是我迄今为止从未想到过的。我还考虑了Python解决方案,但我的Python技能非常有限。
break set -G true -n generateMoreData --skip-prologue false
breakpoint name configure SecondBreakpoint -G true -C "x -o generateMoreData.txt --append-outfile -c \`$size\` $destination" -C "break delete SecondBreakpoint"
break command add 1
> expr long $destination = $arg1
> expr long $size = $arg2
> up
> break set -N SecondBreakpoint -a $pc
> DONE