gdbserver调试共享库(终结版)

本文介绍了一种通过修改gdb代码来实现调试共享库的方法,包括读取/proc/$pid/maps文件获取共享库加载地址、使用bfd查询代码段偏移量、计算符号表地址及加载符号表的过程。

转载时请注明出处和作者联系方式
文章出处:http://www.limodev.cn/blog
作者联系方式:李先静 <xianjimli at hotmail dot com>

我 已经写过两篇关于gdbserver调试共享库的BLOG了:第一篇解决了调试共享库的难题,让调试共享库成为可能,但是使用起来很麻烦。第二篇做了点改 进,通过一个脚本文件计算偏移量,使用起来稍微方便一点。几年过去了,gdbserver还是不支持调试共享库,我也受够了,最终决定去修改了gdb的代 码。其实我们要做的就是计算共享库加载符号表的地址,算法很简单:

共享库加载符号表的地址 = 共享库在内存中的加载地址+代码段的偏移量。

实现原理如下:
o 通过gdbserver读/proc/$pid/maps文件,以获取共享库在内存中的加载地址。
o 通过bfd查询共享库的代码段的偏移量。
o 计算共享库加载符号表的地址。
o 调用add-symbol-file加载符号表。

具体实现如下:

o gdbserver增加qMaps请求(gdb/gdbserver/server.c)

void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
{

if (strncmp (own_buf, "qMaps;", 5) == 0)
{
handle_q_maps (own_buf);
return;
}

}

o gdbserver处理qMaps请求

handle_q_maps (char *own_buf)
{
FILE* fp = NULL;
char file_name[256] = {0};

snprintf(file_name, sizeof(file_name), "/proc/%ld/maps", signal_pid);
if((fp = fopen(file_name, "rb")) != NULL)
{
char line[512] = {0};
while(fgets(line, sizeof(line), fp) != NULL)
{
putpkt(line);
}
putpkt(" ");
fclose(fp);
}

包的大小有限制(2000),所以我们只传一行过去,最后再发” “表示传输结束。

o修改gdb/remote.c解析maps文件。

static Maps* maps_parse(Maps* maps, char* maps_str)
{
char* line = maps_str;
char* next_line = NULL;
while(line != NULL)
{
next_line = strchr(line, '/n');
if(next_line != NULL)
{
*next_line = '/0';
next_line++;
}

if(strstr(line, "r-xp") != NULL && strstr(line, ".so") != NULL)
{
int unused = 0;
MapsItem item;
memset(&item, 0x00, sizeof(item));

sscanf(line, "%08x-%08x r-xp %08x %02x:%02x %d %s",
&(item.start), &(item.end), &unused, &unused, &unused, &unused, item.file_name);
maps_add(maps, &item);
}
line = next_line;
}

return maps;
}

o修改gdb/remote.c获取代码段的偏移量。

int remote_get_so_vma(const char* file_name, size_t* vma)
{
char **matching = NULL;
bfd* b = bfd_openr(file_name, NULL);

if(b != NULL && bfd_check_format_matches (b, bfd_object, &matching))
{
asection* s = bfd_get_section_by_name(b, ".text");
if(s != NULL)
{
*vma = s->vma;

return 1;
}
}

return 0;
}

o 修改gdb/symfile.c实现add_shared_symbol_files_command,这个函数在linux下没有实现。

static void
add_shared_symbol_files_command (char *args, int from_tty)
{
#ifdef ADD_SHARED_SYMBOL_FILES
ADD_SHARED_SYMBOL_FILES (args, from_tty);
#else
/*support sharelib: {*/
extern void remote_list_so(void);
extern int remote_get_so_nr(void);
extern int remote_get_so_vma(const char* file_name, size_t* vma);
extern int remote_get_so_text_start_addr(const char* file_name, size_t* addr);
extern int remote_get_so_name_text_start_addr(int index, const char** file_name, size_
t* addr);

if (current_target.to_shortname &&
(strcmp (current_target.to_shortname, "remote") == 0
|| strcmp (current_target.to_shortname, "extended-remote") == 0))
{
size_t addr = 0;
char cmd[2048] = {0};
if(args == NULL || args[0] == '/0')
{
printf_unfiltered("available so:/n");
remote_list_so();
printf_unfiltered("usage: add-shared-symbol-files file/n");
printf_unfiltered("usage: add-shared-symbol-files all remote_path:local_path/n");

return;
}
else if(strncmp(args, "all ", 4) == 0)
{
size_t i = 0;
size_t n = 0;
int rlen = 0;
char rpath[260] = {0};
char lpath[260] = {0};
char* p = strchr(args, ':');
const char* file_name = NULL;
if(p == NULL) return;

*p = '/0';
strncpy(lpath, p + 1, sizeof(lpath));
strncpy(rpath, args + 4, sizeof(rpath));

rlen = strlen(rpath);
n = remote_get_so_nr();
for(i = 0; i < n; i++)
{
remote_get_so_name_text_start_addr(i, &file_name, &addr);
if(file_name != NULL && strncmp(file_name, rpath, rlen) == 0)
{
size_t vma = 0;
char lfile_name[512] = {0};

snprintf(lfile_name, sizeof(lfile_name),"%s/%s", lpath, file_name+rlen);
if(remote_get_so_vma(lfile_name, &vma))
{
addr += vma;
}
snprintf(cmd, sizeof(cmd),"%s/%s %p", lpath, file_name+rlen, (void*)addr);
add_symbol_file_command(cmd, 0);
}
}
}
else
{
remote_get_so_text_start_addr(args, &addr);

if(addr > 0)
{
size_t vma = 0;
if(remote_get_so_vma(args, &vma))
{
addr += vma;
}
snprintf(cmd, sizeof(cmd), "%s %p", args, (void*)addr);
add_symbol_file_command(cmd, from_tty);
}
else
{
printf_unfiltered("%s is not found./n", args);
}
}
return;
}
/*support sharelib: }*/
error (_("This command is not available in this configuration of GDB."));
#endif
}

使用方法:
o 加载单个共享库的符号表。add-shared-symbol-files 共享库文件名(本地绝对路径).
o 加载多个共享库的符号表。add-shared-symbol-files all 远程路径:本地路径

因为加载符号表时是加载本地文件,所以加载多个文件时,要把远程路径替换成本地路径。

需要代码的朋友请到这里(三个修改的文件)下载。


参考:
gdb编译方法:http://blog.youkuaiyun.com/absurd/archive/2006/08/19/1097392.aspx
用gdbserver调试共享库(改进版): http://blog.youkuaiyun.com/absurd/archive/2007/09/20/1793646.aspx


欢迎到Linux mobile development上交流


<think>好的,我现在需要帮助用户了解如何配置GDBServer进行调试。根据用户提供的引用资料,我需要整理出一个清晰的步骤指南。首先,我得回顾用户提供的四个引用内容,确保覆盖所有关键点。 引用1提到在服务器上运行gdb并检查架构,这可能涉及到确保目标平台和宿主机架构兼容。引用2和3讨论了远程调试的两种方式,其中使用gdbserver是重点,并指出需要针对ARM平台重新编译gdb。引用4详细描述了网络配置、权限设置和调试启动的具体命令。这些步骤需要整合成一个连贯的流程。 首先,用户需要安装gdbserver在目标板上,同时宿主机安装交叉编译的gdb。然后配置网络,确保两者可以通信。接着,编译应用程序时加入调试信息,复制到目标板并赋予执行权限。启动gdbserver监听端口,宿主机连接并进行调试。可能还需要处理不同架构的情况,比如ARM平台需要特定版本的gdb,如引用3所述。 需要注意用户可能的误区,比如直接使用系统自带的gdb而非交叉编译版本,或者网络配置不正确导致连接失败。此外,权限问题也是常见错误,比如没有给程序添加可执行权限。 最后,生成相关问题时要围绕配置步骤、常见问题、优化方法和应用场景展开,帮助用户深入理解。</think>### GDBServer调试配置方法详解 #### **一、环境准备** 1. **目标板(开发板/嵌入式设备)** - 安装gdbserver:若系统未预装,需交叉编译gdbserver并上传至设备 示例命令:`gdbserver --version` 验证是否安装成功[^3] - 网络连通性:确保设备与宿主机IP互通(可通过`ping`命令测试)[^4] 2. **宿主机(调试用电脑)** - 安装交叉编译工具链:生成与目标平台匹配的gdb(如ARM平台需`arm-linux-gnueabihf-gdb`) 编译参数示例:`./configure --target=arm-linux --prefix=/opt/arm-gdb`[^3] - 验证工具链:`arm-linux-gnueabihf-gdb -v` 查看版本信息 #### **二、调试流程** 1. **编译带调试信息的程序** ```bash arm-linux-gnueabihf-gcc -g -o hello_world hello_world.c # -g参数保留符号表 ``` ▸ 关键点:必须使用与目标板匹配的交叉编译器 2. **部署到目标板** ```bash scp hello_world root@192.168.1.100:/root # 假设目标板IP为192.168.1.100 ssh root@192.168.1.100 chmod +x /root/hello_world # 添加可执行权限 ``` 3. **启动gdbserver** ```bash gdbserver :10000 ./hello_world # 监听10000端口 ``` ▸ 输出提示:`Listening on port 10000`表示服务已就绪 4. **宿主机连接调试** ```bash arm-linux-gnueabihf-gdb ./hello_world (gdb) target remote 192.168.1.100:10000 # 建立远程连接 (gdb) b main # 设置断点 (gdb) continue # 开始执行 ``` #### **三、调试技巧** 1. **多架构支持验证** ```bash (gdb) show architecture # 查看当前调试架构[^1] set architecture armv7 # 手动指定架构 ``` 2. **文件传输优化** - 使用`gdbserver --attach`附加到正在运行的进程 - 通过`nfs`共享目录避免重复传输 3. **断点高级用法** ```bash (gdb) watch variable_name # 数据断点 (gdb) catch throw # 捕获异常 ``` #### **四、常见问题处理** | 问题现象 | 解决方案 | |---------|----------| | 连接超时 | 检查防火墙设置:`iptables -L` | | 架构不匹配 | 重新编译gdb时指定`--target=armv5te` | | 符号缺失 | 确认编译时使用`-g`参数且未执行strip操作 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值