GNU Binutils简介
GNU为GCC编译器提供了配套的辅助工具集(Binutils),具体参考:https://www.gnu.org/software/binutils/
GCC大写表示一整套工具集,小写gcc只代表编译器。
常见工具简介
工具名 | 功能简介 |
add2line | 将代码地址转化为对应的行号 |
strip | 剔除可执行程序中 的调试信息 |
ar | 将目标文件打包成为静态库 |
nm | 列出目标文件中的符号以及对应地址 |
objdump | 查看程序段信息以及反汇编 |
size | 查看目标文件中的段大小 |
strings | 查看目标文件中的字符串 |
add2line
将指定地址转换为对应的文件名和行号。
经常用于分析和定位内存访问错误的问题。
void func()
{
*g_pointer = (int)"SoftWare";
return;
}
//比如,第三行代码,如果g_pointer为空的话,就肯定会造成程序崩溃。
使用步骤
addr2line 示例:定位0地址访问
开启core dump 选项,ulimit -c unlimited
运行程序,并生成崩溃时core文件,执行导致程序崩溃的测试用例
读取core文件,获取IP寄存器的值(0x08048000),dmesg core
使用addrline 定位代码行,addr2line 0x08048000 -f -e test.out
看一下如下demo:
//func.c
#include <stdio.h>
int* g_pointer;
void func()
{
*g_pointer = (int)"SoftWare";
return;
}
//test.c
#include <stdio.h>
int g_global = 0;
int g_test = 1;
extern int* g_pointer;
extern void func();
int main(int argc,char* argv[])
{
printf("&g_global = %p\n",&g_global);
printf("&g_test = %p\n",&g_test);
printf("&g_pointer = %p\n",&g_pointer);
printf("g_pointer = %p\n",g_pointer);
printf("&func = %p\n",&func);
printf("&main = %p\n",&main);
func();
return 0;
}
编译运行:
delphi@delphi-vm:~/DT_SiFang$ gcc -g func.c test.c -o test.out
delphi@delphi-vm:~/DT_SiFang$ ./test.out
&g_global = 0x804a020
&g_test = 0x804a014
&g_pointer = 0x804a024
g_pointer = (nil)
&func = 0x80483c4
&main = 0x80483d8
段错误
delphi@delphi-vm:~/DT_SiFang$
使用add2line 进行定位:
delphi@delphi-vm:~/DT_SiFang$ ulimit -c unlimited
delphi@delphi-vm:~/DT_SiFang$ ./test.out
&g_global = 0x804a020
&g_test = 0x804a014
&g_pointer = 0x804a024
g_pointer = (nil)
&func = 0x80483c4
&main = 0x80483d8
段错误 (核心已转储)
delphi@delphi-vm:~/DT_SiFang$ ls
core func.c test.c test.out
delphi@delphi-vm:~/DT_SiFang$ dmesg core
这样就可以定位出来func.c文件第7行,func函数出问题了。
strip
剔除程序文件中的调试信息,减少目标程序的大小
一般在程序发布前都需要将调试信息剔除
过多的调试信息可能影响程序的执行效率
用法:strip test.out
注意事项
几乎所有的调试辅助工具都依赖于目标文件中调试信息。
调试信息的运用能够快速定位问题。
使用gcc编译程序时使用-g选项生成调试信息。
发布程序时再考虑是否使用strip剔除调试信息。
ar
打包目标文件
ar crs libname.a x.o y.o
解压目标文件
ar x libname.a
nm
列出目标文件中的标识符(变量名,函数名)
输出结果由三部分组成:{地址,段,标识符}
示例:
08048430(标识符对应的地址) T(标识符位于代码段) func(标识符的名字)
段标识说明
段标识 | 说明 |
A | 地址值在链接过程中不会发生改变 |
B或b | 标识符位于未初始化数据段(.bss) |
C | 未定义存储段的标识符,链接时决定段位置 |
D或d | 标识符位于数据段(.data) |
N | 调试专用标识符 |
R或r | 标识符位于只读存储区(.rdata) |
T 或 t | 标识符位于代码段(.text) |
U | 未定义的标识符 |
objdump
反汇编目标文件,查看汇编到源码的映射
objdump -d func.o
objdump -S func.o
查看目标文件中的详细段信息
objdump -h test.out
objdump -h的输出说明
说明 | |
Idx | 段下标 |
Name | 段标识符(名字) |
Size | 段所占空间的大小 |
VMA | 段起始位置的虚存地址 |
LMA | 段在存储空间中的加载地址 |
File off | 段在目标文件中的相对位置 |
Algn | 段的边界对齐字节数 |
size
获取目标文件中的所有段大小 size test.out
strings
获取目标文件中的所有字符串常量 strings test.out