【1】objdump 反汇编
objdump命令是Linux下的反汇编目标文件或者可执行文件的命令,它以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息。
参数选项:
- -d:将代码段反汇编
- -S:将代码段反汇编的同时,将反汇编代码和源代码交替显示,编译时需要给出-g,即需要调试信息
- -C:将C++符号名逆向解析
- -l:反汇编代码中插入源代码的文件名和行号
- -j section:仅反汇编指定的section。可以有多个-j参数来选择多个section
<代码示例>
class Main
{
int a;
public:
Main()
{
a = 0;
}
int getA(void)
{
return this->a;
}
};
$objdump -d a.out # 简单反汇编
08048456 <_ZN4MainC1Ev>:
8048456: 55 push %ebp
8048457: 89 e5 mov %esp,%ebp
8048459: 8b 45 08 mov 0x8(%ebp),%eax
804845c: c7 00 00 00 00 00 movl $0x0,(%eax)
8048462: 5d pop %ebp
8048463: c3 ret
08048464 <_ZN4Main4getAEv>:
8048464: 55 push %ebp
8048465: 89 e5 mov %esp,%ebp
8048467: 8b 45 08 mov 0x8(%ebp),%eax
804846a: 8b 00 mov (%eax),%eax
804846c: 5d pop %ebp
804846d: c3 ret
$objdump -S a.out # 反汇编代码中混入对应的源代码
08048456 <_ZN4MainC1Ev>:
class Main
{
int a;
public:
Main()
8048456: 55 push %ebp
8048457: 89 e5 mov %esp,%ebp
{
a = 0;
8048459: 8b 45 08 mov 0x8(%ebp),%eax
804845c: c7 00 00 00 00 00 movl $0x0,(%eax)
}
8048462: 5d pop %ebp
8048463: c3 ret
08048464 <_ZN4Main4getAEv>:
int getA(void)
8048464: 55 push %ebp
8048465: 89 e5 mov %esp,%ebp
{
return this->a;
8048467: 8b 45 08 mov 0x8(%ebp),%eax
804846a: 8b 00 mov (%eax),%eax
}
804846c: 5d pop %ebp
804846d: c3 ret
$objdump -C -S a.out # C++符号名逆向解析
08048456 <Main::Main()>:
class Main
{
int a;
public:
Main()
8048456: 55 push %ebp
8048457: 89 e5 mov %esp,%ebp
{
a = 0;
8048459: 8b 45 08 mov 0x8(%ebp),%eax
804845c: c7 00 00 00 00 00 movl $0x0,(%eax)
}
8048462: 5d pop %ebp
8048463: c3 ret
08048464 <Main::getA()>:
int getA(void)
8048464: 55 push %ebp
8048465: 89 e5 mov %esp,%ebp
{
return this->a;
8048467: 8b 45 08 mov 0x8(%ebp),%eax
804846a: 8b 00 mov (%eax),%eax
}
804846c: 5d pop %ebp
804846d: c3 ret
$objdump -j .text -l -C -S a.out # 打印源文件名和行号
08048456 <Main::Main()>:
_ZN4MainC2Ev():
~/objdump/main.h:5
class Main
{
int a;
public:
Main()
8048456: 55 push %ebp
8048457: 89 e5 mov %esp,%ebp
~/objdump/main.h:7
{
a = 0;
8048459: 8b 45 08 mov 0x8(%ebp),%eax
804845c: c7 00 00 00 00 00 movl $0x0,(%eax)
~/objdump/main.h:8
}
8048462: 5d pop %ebp
8048463: c3 ret
08048464 <Main::getA()>:
_ZN4Main4getAEv():
~/objdump/main.h:10
int getA(void)
8048464: 55 push %ebp
8048465: 89 e5 mov %esp,%ebp
~/objdump/main.h:12
{
return this->a;
8048467: 8b 45 08 mov 0x8(%ebp),%eax
804846a: 8b 00 mov (%eax),%eax
~/objdump/main.h:13
}
804846c: 5d pop %ebp
804846d: c3 ret
关于符号表字段下面直接只介绍部分常用的:
.text:已编译程序的机器代码。
.rodata:只读数据,比如printf语句中的格式串和开关(switch)语句的跳转表。
.data:已初始化的全局C变量。局部C变量在运行时被保存在栈中,既不出现在.data中,也不出现在.bss节中。
.bss:未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率在:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。
.symtab:一个符号表(symbol table),它存放在程序中被定义和引用的函数和全局变量的信息。一些程序员错误地认为必须通过-g选项来编译一个程序,得到符号表信息。实际上,每个可重定位目标文件在.symtab中都有一张符号表。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的表目。
【2】addr2line 通过指令地址定位到行
我们在linux下开发程序时,经常遇到Segmentation fault的错误信息,如果代码比较少还可以去一行行排除,但是代码量比较大的时候,往往是一件头疼的事情。Segmentation fault会有地址抛出,我们可以利用addr2line定位到程序出错的行,这样是不是很方便的排查问题。下面就简单的介绍一下addr2line的使用方法。
编写测试代码故意产生Segmentation fault。
①gcc编译测试代码,注意需要增加-g选项 !
②dmesg查看系统崩溃的信息,查找崩溃的地址!
③使用addr2line命令查看错误信息
【3】dmesg命令显示开机信息
在dmesg里我们可以查看到开机信息,printk产生的信息等。若研究内核代码,在代码中插入printk函数,然后通过dmesg观察是一个很好的方法。
(1)dmesg输出含义
终端输入dmesg,可以看到每行最开始显示的是一个中括号,里面的数字为timestamp(时间戳),该时间指示的系统从开机到现在的运行时间,单位为s 秒。
(2)dmesg -C
在显示的同时,clean掉dmesg缓存中信息
(3)dmesg -T
以当前时间的方式显示时间信息,而不是图1中所示的开机时间
(4) dmesg -d
显示dmesg中两条打印信息的时间间隔
(5)dmesg -d -T
-d和-T参数混合使用,效果可想而知
(6)dmesg | tail 显示dmesg最近一次的输出
由上面我们可以看到dmesg可以让我们获得很多信息,包括函数进入时间等,可以利用dmesg原本已有的功能进行辅助分析。dmesg 很~\(≧▽≦)/~赞!
【4】-g、-ggdb、-g3和-ggdb3之间的区别
-g和-ggdb之间只有细微的区别。
具体来说,-g产生的debug信息是OS native format, GDB可以使用之。而-ggdb产生的debug信息更倾向于给GDB使用的。所以,如果你用的GDB调试器,那么使用-ggdb选项。如果是其他调试器,则使用-g。
3只是级别。这个级别会产生更多的额外debug信息。3这个级别可以调试宏。