#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { int n = 0x3fc00000; printf("%fn", n); return 0; }
$ cc main.c -g main.c: In function ‘main’ main.c:9: warning: format ‘%f’ expects type ‘double’, but argument 2 has type ‘ $ gdb a.out Reading symbols from /home/dutor/Wdir/Cpp/GDB/a.out...done. (gdb) l 1 #include 2 #include 3 #include 4 5 int 6 main() 7 { 8 int n = 0x3fc00000; 9 printf("%fn", n); 10 return 0; (gdb) 11 } (gdb) start Temporary breakpoint 1 at 0x80483cd: file main.c, line 8. Starting program: /home/dutor/Wdir/Cpp/GDB/a.out Temporary breakpoint 1, main () at main.c:8 8 int n = 0x3fc00000; (gdb) i program Using the running image of child process 7519. Program stopped at 0x80483cd. It stopped at a breakpoint that has since been deleted. (gdb) n 9 printf("%fn", n); (gdb) p/x n $2 = 0x3fc00000 (gdb) p/f n $3 = 1.5 (gdb) disassemble Dump of assembler code for function main: 0x080483c4 <+0>: push %ebp 0x080483c5 <+1>: mov %esp,%ebp 0x080483c7 <+3>: and $0xfffffff0,%esp 0x080483ca <+6>: sub $0x20,%esp 0x080483cd <+9>: movl $0x3fc00000,0x1c(%esp) # 为n赋值 => 0x080483d5 <+17>: mov $0x80484c0,%eax # 格式字符串地址 0x080483da <+22>: mov 0x1c(%esp),%edx 0x080483de <+26>: mov %edx,0x4(%esp) # printf参数入栈 0x080483e2 <+30>: mov %eax,(%esp) 0x080483e5 <+33>: call 0x80482f4 0x080483ea <+38>: mov $0x0,%eax 0x080483ef <+43>: leave 0x080483f0 <+44>: ret End of assembler dump. (gdb) si 0x080483da 9 printf("%fn", n); (gdb) display /i $eip # 每次暂停都打印下一条指令 2: x/i $eip => 0x80483da : mov 0x1c(%esp),%edx (gdb) x/s $eax 0x80484c0: "%fn" (gdb) si 0x080483de 9 printf("%fn", n); 2: x/i $eip => 0x80483de : mov %edx,0x4(%esp) (gdb) 0x080483e2 9 printf("%fn", n); 2: x/i $eip => 0x80483e2 : mov %eax,(%esp) (gdb) 0x080483e5 9 printf("%fn", n); 2: x/i $eip => 0x80483e5 : call 0x80482f4 (gdb) x/x $esp 0xbfffec30: 0x080484c0 (gdb) x/xw $esp+4 0xbfffec34: 0x3fc00000 (gdb) x/fw $esp+4 0xbfffec34: 1.5 (gdb) c Continuing. 0.000000 Program exited normally. (gdb) q $
我们发现,传给printf的整数n的正是浮点数1.5的二进制形式(0x3fc00000参考IEEE 754浮点数标准),但为什么输出确实0.000000呢?同样的方法调试下面程序,
int main() { printf("%fn", 1.5); return 0; }
会发现这次传递给printf的是8字节的0x3fff 8000 0000 0000,是1.5的双精度表示!可见gcc中printf对格式字符串中的f是按双精度对待的(I’m wondering: how about a single float?)
那么下面的程序的输出就应该是1.5了,你不妨试试
int main() { printf("%fn", 0x0, 0x3fff8000); return 0; }
调试内存转储文件
内存转储文件(core dump)是程序发生严重错误时操作系统产生的文件,它包含了程序崩溃时占用的内存页面的拷贝,因此使用core文件在一定程度上可以再现崩溃前夕程序的状态。
多种原因下程序会崩溃,从而被系统终止,生成core文件,经常见到的是访存错误(段错误,Segment fault)。另外,系统是否生成core文件以及core文件的最大尺寸还受系统参数的控制。例如ubuntu下面普通用户程序是不允许生成core文件的,这可以使用ulimit命令来修改。不带参数的ulimit -c输出当前core文件的最大允许值,为了能够生成core文件,使用ulimit -c size设置大小,简单地,ulimit -c unlimited将其设为没有限制。
void foo(const char* s) { char c = *s; } int main() { foo(0); return 0; }
编译,执行,调试该程序,
$ ulimit unlimited $ cc main.c -g $ ./a.out Segmentation fault (core dumped) # 段错误,生成名为core的core文件 $ gdb a.out core Reading symbols from /home/dutor/Wdir/Cpp/GDB/a.out...done. [New Thread 11199] warning: Can't read pathname for load map: Input/output error. Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done. Loaded symbols for /lib/libc.so.6 Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done. Loaded symbols for /lib/ld-linux.so.2 Core was generated by `./a.out'. Program terminated with signal 11, Segmentation fault. # 程序终止原因 #0 0x0804839d in foo (s=0x0) at main.c:7 # 崩溃时程序执行到第7行foo函数。 7 char c = *s; (gdb) bt #0 0x0804839d in foo (s=0x0) at main.c:7 #1 0x080483b7 in main () at main.c:12 (gdb) p /x s # 噢,s竟然是0! $1 = 0x0 (gdb) q $