远程调试期间,GDB挂起,库版本不匹配。

4

我正在使用Linux并尝试进行远程调试程序。

我从.xinitrc启动gdbserver到目标上,命令如下:

gdbserver localhost:9134 /root/game/game

我在我的本地电脑上,使用虚拟机中运行的方式,通过以下命令从gdb连接到目标:

target remote 192.168.1.20:9134

它能够成功连接。我可以在 main 函数处设置断点。

b main

然后我可以继续操作,但程序会在那里中断。我可以单步执行一段时间,直到调用SDL_Init(),此时它将永远不会返回到gdb。 如果我不单步执行到SDL_Init而是设置一个进程更深的断点,程序将启动并正常运行(所以它能够通过SDL_Init)。但当它到达断点时,它会在目标机器上冻结,我的本地机器上的gdb从未显示提示。整个过程都挂起了,并且必须重新启动。但它并没有完全冻结,因为目标上的鼠标指针仍然移动,您可以对其进行ping测试,但gdb连接不再起作用。因此,似乎图形系统会在某种程度上干扰此过程,因为在图形系统初始化之前的断点确实有效,但在之后就无法使用了。
我尝试将remotetimeout设置为500秒,并出现了相同的行为。当我从本地计算机ping远程目标时,报告的时间约为0.3至0.4毫秒。因此,这似乎并不寻常,但我不能排除任何其他配置有误的网络设置。
这是一个遗留系统(但是,嘿,它仍然赚钱),具有gdbserver版本6.8-19.fc10和gdb版本6.8-29.fc10。升级版本可能是可能的,但可能不必要(我对我的计算机进行的任何升级都必须对州监管机构的系统进行升级,因为他们使用gdb设置进行测试,在此过程中需要升级)。远程调试在我接手之前曾经工作过,之前参与其中的任何人都已离职。gdbserver版本肯定有效,因为我正在使用以前使用的确切程序。
更新1:
我将主机上的gdb版本更新到了7.0.1版本,它仍然表现出相同的行为。我不能使用版本8,因为它需要C++11编译器,而这个遗留系统是在那个时期之前。
更新2:
我在另一个虚拟机中尝试了这个,并且甚至构建了一个全新的专用Linux安装(因此没有虚拟机),重建了软件,但我得到了相同的行为。因此,问题似乎可能在目标机器的配置上。
更新3:
我找到了一个串行电缆,并最终通过串行获得了远程调试设置。它仍然无法正常工作,但它给了我更多的错误消息。我收到了错误信息。
gdbserver: error initializing thread_db library: version mismatch between libthread_db and libpthread

我认为这是有道理的,因为在图形系统初始化后(涉及创建一些线程),我的断点无法工作。通过搜索该错误,我尝试使用set solib-absolute-prefixset solib-search-pathset sysroot将这些变量设置为主机上目标机器文件系统副本的根目录(即在主机上,/fw_dev/fgs/cf/initrd/expand包含initrd所制造的文件系统)。
但是,当我试图设置断点时,我得到了Error accessing memory address 0xb5eb60: Input/output error. 我还尝试将这些变量设置为lib子目录,但也不起作用。我还尝试仅从主机的/lib目录复制本地线程库到目标的/lib,但此时x窗口甚至无法启动。

更新#4:
我尝试从主机上的目标文件系统副本的根目录(/fw_dev/fgs/cf/initrd/expand)启动gdb,gdb仍然挂在断点上,但不再收到关于libthread_db和libpthread不匹配的错误消息,所以又回到了原点。

更新#5:
也许我已经到了应该单独提问的地步,但我编译了gdb,然后在其本身上运行了gbd。然后使用file将它设置为主机上的程序,在远程目标上设置断点,然后运行continue。当我到达断点时,gdb总是挂起。但现在当我在gdb中按下ctrl-c时,我得到这个回溯。

#0  0x00110416 in __kernel_vsyscall ()
#1  0x00b3f39d in ___newselect_nocancel () from /lib/libc.so.6
#2  0x08203b9a in ser_base_wait_for (scb=0x96a2930, timeout=1) at ser-base.c:206
#3  0x08203c89 in do_ser_base_readchar (scb=0x96a2930, timeout=-1) at ser-base.c:256
#4  0x08204046 in generic_readchar (scb=0x96a2930, timeout=-1, do_readchar=0x8203c60 <do_ser_base_readchar>) at ser-base.c:326
#5  0x082040b0 in ser_base_readchar (scb=0x96a2930, timeout=-1) at ser-base.c:391
#6  0x081ecac2 in serial_readchar (scb=0x96a2930, timeout=-1) at serial.c:376
#7  0x080c4357 in readchar (timeout=<value optimized out>) at remote.c:5922
#8  0x080c5e35 in getpkt_or_notif_sane_1 (buf=0x839f140, sizeof_buf=0x839f144, forever=1, expecting_notif=0) at remote.c:6440
#9  0x080d1e0a in getpkt_sane (ops=0x839f180, ptid=..., status=0xbffff388, options=0) at remote.c:6534
#10 remote_wait_as (ops=0x839f180, ptid=..., status=0xbffff388, options=0) at remote.c:4736
#11 remote_wait (ops=0x839f180, ptid=..., status=0xbffff388, options=0) at remote.c:4843
#12 0x08184d4b in target_wait (ptid=..., status=0xbffff388, options=0) at target.c:2098
#13 0x0815daf2 in wait_for_inferior (treat_exec_as_sigtrap=0) at infrun.c:2028
#14 0x0815ddd4 in proceed (addr=4294967295, siggnal=TARGET_SIGNAL_DEFAULT, step=0) at infrun.c:1652
#15 0x08153729 in continue_1 (all_threads=0) at infcmd.c:668
#16 0x08153ea2 in continue_command (args=0x0, from_tty=0) at infcmd.c:760
#17 0x0808e9e8 in execute_command (p=0x83b89a1 "", from_tty=0) at top.c:453
#18 0x0816b028 in command_handler (command=0x83b89a0 "c") at event-top.c:511
#19 0x0816bd5a in command_line_handler (rl=0x8ce83e8 "\340&\266\b\340\230\321\b") at event-top.c:736
#20 0x0822d5a5 in rl_callback_read_char () at callback.c:205
#21 0x0816b17b in rl_callback_read_char_wrapper (client_data=0x0) at event-top.c:178
#22 0x0816ac54 in handle_file_event (data=...) at event-loop.c:812
#23 0x08169e6b in process_event () at event-loop.c:394
#24 0x0816aba4 in gdb_do_one_event (data=0x0) at event-loop.c:459
#25 0x0816500b in catch_errors (func=0x816a950 <gdb_do_one_event>, func_args=0x0, errstring=0x82ccc3d "", mask=6) at exceptions.c:510
#26 0x080f072a in tui_command_loop (data=0x0) at ./tui/tui-interp.c:153
#27 0x08165684 in current_interp_command_loop () at interps.c:291
#28 0x0808653b in captured_command_loop (data=0x0) at ./main.c:226
#29 0x0816500b in catch_errors (func=0x8086530 <captured_command_loop>, func_args=0x0, errstring=0x82ccc3d "", mask=6) at exceptions.c:510
#30 0x08085ecc in captured_main (data=0xbffff7a4) at ./main.c:902
#31 0x0816500b in catch_errors (func=0x80853d0 <captured_main>, func_args=0xbffff7a4, errstring=0x82ccc3d "", mask=6) at exceptions.c:510
#32 0x080851d1 in gdb_main (args=0xbffff7a4) at ./main.c:911
#33 0x08085195 in main (argc=128, argv=0x0) at gdb.c:33

看起来gdb卡在__kernel_vsyscall()里了。在主机的/lib目录和目标机器上的libc.so.6上进行diff后发现不同之处。我试过使用LD_PRELOAD和LD_LIBRARY_PATH,但是backtrace总是显示/lib/libc.so.6,而不是指向目标机器上的拷贝。也许我没有正确设置它们,但我已经尝试在gdb中用set env设置它们,也尝试在命令行上设置并导出它们,但都无济于事。我还尝试将主机计算机的libc放到目标机器上,但它甚至无法启动,它在libc中得到segfault。
那么我该如何让gdb加载不同的库呢?
更新#6:
所以我用目标系统的磁盘映像作为基础制作了一个可启动的USB键。我对它进行了最少的更改,使其在标准PC上运行,并添加了gdb和gdb所需的库。所以现在,主机和目标机器上的libc是相同的,但它仍然卡住了我。
最终结果:虽然我知道gdb 6.8过去可以工作,但我无法找出配置。在升级了所有gdb和gdbserver到7.12之后,它终于可以工作了。

你使用的是哪个Linux发行版?另外,你运行的内核版本是什么? - oxr463
目标设备上运行的内核版本为2.6.27.5-117.fc10.i686。它是基于Fedora开发的,但经过了大量精简,并使用了BusyBox。虽然它使用了一个更或多或少正常的主板,但添加了硬件(GPIO、NVRAM、电池备份入侵检测等),因此可以看作是一个大型嵌入式系统。主机使用32位Fedora 10操作系统,内核版本相同。 - jhufford
dmesg 有什么有趣的信息吗?此外,系统有多少 RAM? - oxr463
在它挂起之后,我无法在目标上运行dmesg,因为GUI已经启动,并且我无法使用键盘将其关闭。我尝试在其中安装sshd,以便在它挂起后远程连接到目标,但这涉及到更多的库问题,导致它出现段错误。 - jhufford
你能否在没有图形用户界面的情况下启动它,然后手动启动它吗? - oxr463
@LucasRamage 我不确定你的意思。但是我可以让目标系统在没有 GUI 的情况下启动,并从 .xinitrc 文件中使用 gdbserver 启动目标程序。 - jhufford
2个回答

1
升级版本虽然很麻烦,但可能是可行的,但不一定必要...这是正确的选择。你遇到的所有其他问题都是由于此原因导致的。
我已经在另一个虚拟机上尝试过了,并且甚至建立了一个全新的专用Linux安装(因此没有虚拟机),重新构建了软件,但仍然出现了相同的行为。因此,问题似乎可能在目标机器的配置上。
您应该在与您尝试部署代码的系统相同的版本、架构等上进行构建。
根据this answer,这可能是由32/64位混合引起的。例如,请检查您是否使用64位进程ID附加到32位二进制文件,或者反之。我还尝试将主机计算机上的libc放入目标计算机,但它甚至无法启动,在libc中得到了segfault。

不要这样做。正如你发现的那样,它不起作用。

那么我该如何让gdb加载不同的库?

根据this question,你可以使用LD_LIBRARY_PATH


在更新#1中,我尽可能地升级了gdb,从版本6到7,但仍然存在同样的问题。由于它是一个没有C++11编译器的遗留系统,我无法使用版本8。在项目早期,我尝试将gcc升级到最新版本,但那立即失败了。 - jhufford
我认为这不可能是32/64位混淆,因为主机和目标都是32位系统。主机正在运行Fedora 10,目标正在运行相同的内核,但使用BusyBox进行了剥离。没有桌面或类似的东西。两个系统上的libc具有相同的名称和版本号,但它们并不相同,因此尝试复制它们失败了,我不确定它们为什么不同,显然是编译方式不同。我尝试过LD_LIBRARY_PATH,但我无法使更新#5中的第1行在回溯中更改。所以除非我没有完全正确,但我遵循我在网上找到的例子。 - jhufford
我正在给你提供悬赏。你确实说升级是答案。我使用升级版本的gdb使其工作了。关键是我忘记了同时升级gdbserver...我知道旧版本曾经可以工作,我们有数十个带有旧版gdbserver的gdb脚本和现有的EEPROM,所以肯定存在某些配置问题。但无论如何,结果就是结果。我只需要在状态调节器构建PC上升级gdb版本即可... - jhufford
糟糕...赏金已过期了...我刚刚让它工作起来。对此我感到非常抱歉...无论如何,我很感激你的帮助。 - jhufford
别担心,很乐意帮忙。这可能与主题无关,但您是如何管理这些设备的呢?也就是说,在生产环境中如何部署软件到它们上面? - oxr463
1
这是为扑克/keno机设计的。没有网络。因此,要部署,技术人员需要物理上安装新媒体。软件集是一个CF卡,一个具有选项ROM的BIOS芯片,用于哈希CF卡的可执行部分和一个EPROM芯片,其中包含要进行比较的哈希值。主板上还有另一个插座,如果存在EPROM并且gdbserver在该芯片上,则游戏软件将启动gdbserver。州监管机构使用它来测试特定场景是否符合软件批准标准。幸运的是,该软件有PC模式,因此我可以在本地进行所有开发工作。 - jhufford

0

这里有一些有趣的建议。你尝试过将gdbserver附加到strace上,看看在挂起期间发生了什么样的活动吗?正如其他人所说 - 这可能是进一步解决问题的好方法。 您可以在目标机器上使用以下命令执行此操作:

strace -p `pidof gdbserver`

当挂起时,发送CONT信号也许会有所帮助:

kill -CONT `pidof gdbserver`

kill -CONT 命令没有任何作用。我不确定 strace 显示的一堆 selectrecvsend 是什么意思。当断点被触发并且一切都停滞不前时,从 strace 中没有任何有趣的信息,只是重复显示 select(7, [6], NULL, [6], {1,0}) = 0 (Timeout) - jhufford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接