使用 gdb 回溯 linux 套接字 - 函数未显示,缓冲区未更新

Backtracing linux socket with gdb - functions not being shown, buffer not updating

如标题中所述,当我尝试在 linux 套接字函数(例如发送等)处设置断点时,gdb 表现得很奇怪。我已经阅读了类似的线程,其中建议使用 debug 参数,但我无法设置它,因为我只是在玩不同的 Linux programs/video 游戏,而且我注意到了相同的行为 - 大多数情况下,发送无法回溯。只有熟悉的“<内存地址> in ??”显示消息,地址本身不指向任何东西(无法检索)。同时,send(或 sendto 等)中的消息缓冲区停留在一个值并且不更新(而所有其他值,如 len,是实时的)。我想这些只是 gdb 的局限性,但如果更有知识的人可以阐明这个问题,我将不胜感激。

编辑:

首先我将列出我的步骤: 例如,我正在尝试回溯 openarena 中的 sendto(免费 linux quake-based 游戏)。特别是 Openarena 不是 ELF 可读的,但我得到与其他 ELF-readable 文件相同的结果。因为它不是 ELF-readable,我只能附加到 运行 进程。所以我输入 gdb /usr/games/openarena -p < process name > ,虽然我很确定二进制路径在这种情况下是多余的 (它仍然说 “0x7ffc8a81ebe0s”:不是可执行格式:无法识别文件格式,但我还是能够列出函数和所有内容)作为旁注,附加会产生此错误:

((( https://forum.manjaro.org/t/critical-bug-gdb-broken-with-last-stable-update/53155

“读取 /lib/x86_64-linux-gnu/libpthread.so.0 的共享库符号时出错:”

然而,就我而言,我仍然能够附加,但程序最终在抱怨找不到线程后崩溃。这也经常发生在从大厅加入服务器时,但这是一个旁注,因为我已经通过 运行 直接从 gdb 测试了程序,这不会产生错误,但仍然导致了这个奇怪的套接字行为。 )))

所以附加到进程后,我输入源脚本,脚本包含:

break sendto
commands 1
backtrace -raw-frame-arguments on 
continue
end

然后我恢复程序,它会实时触发回溯。这是加入服务器后的示例输出:

Thread 1 "ioquake3" hit Breakpoint 1, __libc_sendto (fd=43, buf=0x7ffefc5028f0, len=32, flags=0, addr=..., addrlen=16) at ../sysdeps/unix/sysv/linux/sendto.c:25
25      in ../sysdeps/unix/sysv/linux/sendto.c
#0  __libc_sendto (fd=43, buf=0x7ffefc5028f0, len=32, flags=0, addr=..., addrlen=16) at ../sysdeps/unix/sysv/linux/sendto.c:25
#1  0x00005642f88bc5fc in ?? ()
#2  0x00005642f88baf94 in ?? ()
#3  0x00005642f888b1ee in ?? ()
#4  0x00005642f88779cf in ?? ()
#5  0x00005642f8886572 in ?? ()
#6  0x00005642f88a58bf in ?? ()
#7  0x00005642f886e3f5 in main ()

Thread 1 "ioquake3" hit Breakpoint 1, __libc_sendto (fd=43, buf=0x7ffefc5028f0, len=34, flags=0, addr=..., addrlen=16) at ../sysdeps/unix/sysv/linux/sendto.c:25
25      in ../sysdeps/unix/sysv/linux/sendto.c
#0  __libc_sendto (fd=43, buf=0x7ffefc5028f0, len=34, flags=0, addr=..., addrlen=16) at ../sysdeps/unix/sysv/linux/sendto.c:25
#1  0x00005642f88bc5fc in ?? ()
#2  0x00005642f88baf94 in ?? ()
#3  0x00005642f888b1ee in ?? ()
#4  0x00005642f88779cf in ?? ()
#5  0x00005642f8886572 in ?? ()
#6  0x00005642f88a58bf in ?? ()
#7  0x00005642f886e3f5 in main ()

如您所见,main 和 send 之间没有任何东西,套接字缓冲区卡在同一条消息上,而 len 正在正确更新。我可以执行任何类型的动作,跳跃、射击,输出仍然保持不变。正如我所提到的,我得到与其他应用程序几乎相同的输出。有一些主要的 function/loop,然后什么都没有,然后只有发送功能。

至于我的系统规格,我使用的是 Kubuntu 21.04, GDB 版本为:GNU gdb (Ubuntu 10.1-2ubuntu2) 10.1.90.20210411-git Glibc: Ubuntu GLIBC 2.33-0ubuntu5

我最近从早期的 LTS 版本进行了迁移,我想升级可能并不完全干净...

As you can see, there is nothing between main and the send, the socket buffer is stuck at the same message, while len is updating correctly.

几点:

  1. 没有函数名称(这与“无”不同)。如果没有符号 table,这是预期的。 GDB 使用加载的二进制文件中的符号 table(s) 将地址转换为函数名称。

    如果二进制文件被完全剥离,或者如果代码直接生成到内存中,或者如果二进制文件被解压缩或解密到内存中,那么您需要告诉 GDB 从哪里可以获取符号 table(如果符号table 存在 ,这不是给定的)。

  2. 套接字缓冲区被“卡住”也不一定是意外的:程序很可能正在使用 重复 sendto 调用相同 堆栈缓冲区。像这样:

  while (!error) {
    char buf[4096];
    int n = copy_to(buf);  // fill buf[] with data
    if (sendto(fd, buf, n, ...) != n) // handle error  
  }

更新:

I still don't quite understand why I can't see buffer values change.

您不是在查看缓冲区 contents,而是在查看缓冲区 address(即 &buf[0]上面的代码)。

如果您想查看缓冲区内容,您需要打印/检查它。例如。要检查发送的前 8 个字节,请将其添加到您的断点命令中:x/8cx buf。但也要注意,所有发送的数据包都有一个固定的前缀是很常见的,并且不能保证每个数据包的前 8 个字节都会改变。