#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 $