问题:已知
struct A
{
char a:2;
unsigned int b:3;
} a;
a.a=6;
a.b=7;
int b,c;
b=a.a;
c=a.b;
调试分析为何输出b为0xFFFFFFE,c为0x7?
下面分析上述问题;
目的:
1.熟悉 gdb调试命令及汇编环境下调试。
2.学会在Windows的GDB调试Linux的应用程序及objdump调试。
3.分析程序的单步执行,确认每一步的原理;
4.看调试器是如何取得位域的值,如何修改位域的值
环境:
计算机(Windows8.1)、gdb-7.12.1、VMware虚拟机(Ubuntu)、eclipse
1、分析程序的单步执行,确认每一步的原理。
(1)通过eclipse打开程序,编译调试。点击window,选择show view 选项,在子菜单中选择Disassembly选项,查看源码的汇编语言。
同时点击Register选项,方便查看寄存器变量值。
(2)得到源码的汇编代码,单步调试程序,通过汇编语言和寄存器变量的变化确认每一步程序执行的原理。查看eax值在单步机器指令执行后的变化可以采用gdb中的si命令和i r命令,将执行结果输出到exercise1pot1.txt中。
其中部分结果截图如下:
|
15 a.a=6; |
详解 |
eax的值 |
|
movzbl 0x14(%esp),%eax |
将0x14的第一个字节以高位补0的方式放入eax寄存器中 |
0x0 |
|
and $0xfffffffc,%eax |
将eax中的值与0xfffffffc进行位与操作,将结果存入eax中。所以此操作是将eax的最低2位清空 |
0x0 |
|
or $0x2,%eax |
将eax中的值与10进行位或操作,将结果存入eax中。也就是给eax的最低2位中写入10。 |
0x2 |
|
mov %al,0x14(%esp) |
将al(eax的最低8位:0000 0010)中的值回写到0x14的第一个字节中 |
0x2 |
第一条赋值语句a.a=6;执行完成。根据汇编语言可以看出这一段实现了截断,再联系前面可知,是由于char a:2;位域a只占了2个比特位。6(110)赋值下来变成了2(10)。再看下一句赋值语句:
|
16 a.b=7; |
详解 |
eax的值 |
|
movzbl 0x14(%esp),%eax |
将0x14的第一个字节以高位补0的方式放入eax寄存器中 |
0x2 |
|
or $0x1c,%eax |
将eax中的值与11100进行位或操作,将结果存入eax中。也就是给eax的2-4位中写入111。 |
0x1e |
|
mov %al,0x14(%esp) |
将al(eax的最低8位:0001 1110)中的值回写到0x14的第一个字节中 |
0x1e |
第二条赋值语句a.b=7;执行完成。根据汇编语言可以看出这一段实现赋值,而没有发生上面出现的截断,再联系前面可知,是由于char b:3;位域b只占了3个比特位。7(111)的二进制表示刚好是3位。再看下一句赋值语句:
|
18 c=a.a; |
详解 |
eax的值 |
|
movzbl 0x14(%esp),%eax |
将0x14的第一个字节以高位补0的方式放入eax寄存器中 |
0x1e |
|
shl $0x6,%eax |
将eax寄存器中的值左移6位放入eax寄存器中 |
0x780 |
|
sar $0x6,%al |
将eax寄存器中低八位的值右移6位放入eax寄存器中 |
0x7fe |
|
movsbl %al,%eax |
将al寄存器的内容进行符号扩展后放置到edx寄存器中 |
0xfffffffe |
|
mov %eax,0x18(%esp) |
将寄存器eax中的内容(a的地址)传给寄存器中的变量c |
0xfffffffe |
第三条赋值语句c=a.a执行完成。注意:
2.查看调试器是如何取得位域的值,如何修改位域的值。
打开linux终端进行objdump调试,将调试内容输出到exercise1pot1.txt文件中。并打开eclipse查看内存及esp的值。
通过这里可知结构体大小为四个字节,位域a和b,c和d在结构体中这样存储:
|
b=7 |
a=-2 'þ' |
||||||
|
1E(16进制) |
|||||||
|
0 |
0 |
0 |
1 |
1 |
1 |
1 |
0 |
|
c |
d |
||||||
|
FE |
FF |
FF |
FF |
07 |
00 |
00 |
00 |
esp值为0xbffff0a0,c的位置为esp+24,大小为4个字节,d的位置存储在esp+28,大小为4个字节,同时可以发现是应该以小端模式来存放的。
从这里可知c和d的类型都是signed int ;
由此可以推导位域a类型为signed char型,b类型为unsigned int型。