一种让 GDB 从本地 sysroot 和远程 gdbserver 加载库的方法
A way to have GDB load libraries from local sysroot and remote gdbserver
当使用 gdbserver 进行远程调试时,我想让 gdb 从本地 sysroot 加载正在调试的程序的一些共享库,但也允许从 gdbserver 下载功能加载其他的,这些在系统根。
看来gdb只能使用一种方法来查找库、本地文件或远程下载,不能同时使用这两种方法。
例如,如果我将 sysroot 设置为 target:/
以使用远程文件,将下载所有内容:
(gdb) set sysroot target:/
(gdb) run
Starting program:
Reading /root/a.out from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
Reading /root/a.out from remote target...
Reading symbols from target:/root/a.out...
Reading /lib/ld-linux-armhf.so.3 from remote target...
不幸的是,远程系统上的系统库没有调试符号。这是一个闪存有限的嵌入式系统space。调试符号会大大增加文件系统的总大小,根本不适合。
但是,我有一个包含所有系统库的本地 sysroot 树,其中包含调试符号。但是如果我将sysroot设置为这棵树,gdb将不再考虑远程下载。
(gdb) set sysroot /bsp/sysroot
(gdb) run
Starting program:
Reading symbols from /bsp/sysroot/root/a.out...
warning: Could not load shared library symbols for /lib/libm.so.6.
在此示例中,libm.so.6 不在 sysroot 中,但可以从目标下载。但是现在似乎有办法将 target:/
添加回搜索路径。放在solib-search-path里没有效果。
这种情况是由于在嵌入式系统开发中使用板级支持包 (BSP) 引起的。 BSP 包含许多库,这些库在目标上被剥离,因为它们不适合其他情况,但为主机提供了未剥离的副本。 BSP 的用户构建他们自己的软件,但该软件不是 BSP 的一部分,也不存在于 BSP 的 sysroot 中。但是它在目标系统上。
似乎没有办法告诉 gdb 首先尝试在本地查找库,如果找不到则回退到远程下载。
GDB 不支持多个 sysroot,但有一些解决方法。
- 解决此问题的一种方法是修补 GDB 以实际支持多个 sysroot。
gdb/solib.c
中的函数 solib_find_1
(link to source code) handles the library lookup. Currently, it checks the sysroot
(and if that starts with target:
, uses the libraries from the target). Otherwise it takes the basename (i.e. takes the file name from the given absolute path) and looks up the libraries in solib-search-path
.
为了获得所需的行为,solib_find_1
函数应该回退到不同的目录,例如这个补丁:https://gitlab.com/gbenson/binutils-gdb/commit/0ebe17076406a85a35eb0c4f362850ed9efb843e
- 无需修补 GDB 的更简单方法:将目标库复制到主机。变体包括:
- 直接在本地 sysroot 中。这既快速又简单。
- 在本地 sysroot 中使用指向目标库位置的符号链接。
- 使用合并多个位置的文件系统,例如OverlayFS。
- 使用 bind mount 在 sysroot 上安装目录或文件。
- 如果您不想复制文件:从目标建立库的自动本地镜像(例如使用
sshfs
),并使用上一点的方法回退到目标需要。
- 如果您不能更改文件系统,那么您可以在 GDB 客户端和 GDB 服务器之间放置一个代理:
- 使用
set sysroot target:
- 为GDB's File-I/O Remote Protocol创建一个代理,让它透明地转发每条消息,除了对目标文件的请求。如果感兴趣的文件在本地可用,请回复。否则按原样转发消息。
- 使用
target remote :[your_proxy_port]
而不是 target remote :[actual_gdbserver_port]
附加调试器,让您的代理代表您连接到 gdbserver。
我发现 "Copy the target libraries to the host" 变体的组合是最有效的,因为它的要求最低(我只需要一种从目标 copy/receive 文件一次的方法):
- 我将系统库从目标复制到本地目录。我实际上复制了所有这些,所以我不必找出我真正需要的是哪一个。
- 我在应用程序的目标上重新创建了目录结构,并在我的项目的构建输出目录中添加了一个符号链接。使用符号链接努力使设置工作而无需重复维护。
sysroot 的内容实际上不必与目标的远程文件相同:目标已经剥离了没有调试符号的库,而我在 sysroot 中使用了未剥离的库。
当使用 gdbserver 进行远程调试时,我想让 gdb 从本地 sysroot 加载正在调试的程序的一些共享库,但也允许从 gdbserver 下载功能加载其他的,这些在系统根。
看来gdb只能使用一种方法来查找库、本地文件或远程下载,不能同时使用这两种方法。
例如,如果我将 sysroot 设置为 target:/
以使用远程文件,将下载所有内容:
(gdb) set sysroot target:/
(gdb) run
Starting program:
Reading /root/a.out from remote target...
warning: File transfers from remote targets can be slow. Use "set sysroot" to access files locally instead.
Reading /root/a.out from remote target...
Reading symbols from target:/root/a.out...
Reading /lib/ld-linux-armhf.so.3 from remote target...
不幸的是,远程系统上的系统库没有调试符号。这是一个闪存有限的嵌入式系统space。调试符号会大大增加文件系统的总大小,根本不适合。
但是,我有一个包含所有系统库的本地 sysroot 树,其中包含调试符号。但是如果我将sysroot设置为这棵树,gdb将不再考虑远程下载。
(gdb) set sysroot /bsp/sysroot
(gdb) run
Starting program:
Reading symbols from /bsp/sysroot/root/a.out...
warning: Could not load shared library symbols for /lib/libm.so.6.
在此示例中,libm.so.6 不在 sysroot 中,但可以从目标下载。但是现在似乎有办法将 target:/
添加回搜索路径。放在solib-search-path里没有效果。
这种情况是由于在嵌入式系统开发中使用板级支持包 (BSP) 引起的。 BSP 包含许多库,这些库在目标上被剥离,因为它们不适合其他情况,但为主机提供了未剥离的副本。 BSP 的用户构建他们自己的软件,但该软件不是 BSP 的一部分,也不存在于 BSP 的 sysroot 中。但是它在目标系统上。
似乎没有办法告诉 gdb 首先尝试在本地查找库,如果找不到则回退到远程下载。
GDB 不支持多个 sysroot,但有一些解决方法。
- 解决此问题的一种方法是修补 GDB 以实际支持多个 sysroot。
gdb/solib.c
中的函数solib_find_1
(link to source code) handles the library lookup. Currently, it checks thesysroot
(and if that starts withtarget:
, uses the libraries from the target). Otherwise it takes the basename (i.e. takes the file name from the given absolute path) and looks up the libraries insolib-search-path
.
为了获得所需的行为,solib_find_1
函数应该回退到不同的目录,例如这个补丁:https://gitlab.com/gbenson/binutils-gdb/commit/0ebe17076406a85a35eb0c4f362850ed9efb843e - 无需修补 GDB 的更简单方法:将目标库复制到主机。变体包括:
- 直接在本地 sysroot 中。这既快速又简单。
- 在本地 sysroot 中使用指向目标库位置的符号链接。
- 使用合并多个位置的文件系统,例如OverlayFS。
- 使用 bind mount 在 sysroot 上安装目录或文件。
- 如果您不想复制文件:从目标建立库的自动本地镜像(例如使用
sshfs
),并使用上一点的方法回退到目标需要。 - 如果您不能更改文件系统,那么您可以在 GDB 客户端和 GDB 服务器之间放置一个代理:
- 使用
set sysroot target:
- 为GDB's File-I/O Remote Protocol创建一个代理,让它透明地转发每条消息,除了对目标文件的请求。如果感兴趣的文件在本地可用,请回复。否则按原样转发消息。
- 使用
target remote :[your_proxy_port]
而不是target remote :[actual_gdbserver_port]
附加调试器,让您的代理代表您连接到 gdbserver。
- 使用
我发现 "Copy the target libraries to the host" 变体的组合是最有效的,因为它的要求最低(我只需要一种从目标 copy/receive 文件一次的方法):
- 我将系统库从目标复制到本地目录。我实际上复制了所有这些,所以我不必找出我真正需要的是哪一个。
- 我在应用程序的目标上重新创建了目录结构,并在我的项目的构建输出目录中添加了一个符号链接。使用符号链接努力使设置工作而无需重复维护。
sysroot 的内容实际上不必与目标的远程文件相同:目标已经剥离了没有调试符号的库,而我在 sysroot 中使用了未剥离的库。