操作系统:ubuntu 13.04
工具:gcc, objdump, readelf
参考:《程序员自我修养》
代码编译后的机器指令经常被放在代码段里,代码段名为".text";已初始化的全局变量和已初始化的局部静态变量经常放在数据段里,数据段名为".data";未初始化的全局变量和未初始化局部静态变量一般放在“.bss”段里,.bss在文件中不占据空间。字符串常量一般放在“.rodata”段里。
通过代码编译后查看文件内部结构来论证一下上面观点,代码如下:
代码:
-
int printf(const char* format, ...);
-
-
int global_init_var = 84; //已初始化的全局变量
-
int global_uninit_var; //未初始化的全局变量
-
char *str1 = "hello world!"; //字符串常量
-
-
void func1(int i)
-
{
-
printf("%d\n", i);
-
}
-
-
int main(void)
-
{
-
static int static_var = 85; //已初始化的静态局部变量
-
static int static_var2; //未初始化的静态局部变量
-
char *str2 = "22222"; //字符串常量
-
-
int a = 1;
-
int b;
-
-
func1(static_var+static_var2+a+b);
-
-
return a;
-
}
上面代码保存为1.c,编译生成目标文件1.o:
使用objdump来查看目标文件的结构和内容,命令如下:
目标文件结构和内容如下(只保留.bss段、.text段、.data段、.rodata段):
-
1.o: file format elf32-i386
-
-
Contents of section .text:
-
0000 5589e583 ec188b45 08894424 04c70424 U......E..D$...$
-
0010 0d000000 e8fcffff ffc9c355 89e583e4 ...........U....
-
0020 f083ec20 c7442414 11000000 c7442418 ... .D$......D$.
-
0030 01000000 8b150800 0000a100 00000001 ................
-
0040 c28b4424 1801c28b 44241c01 d0890424 ..D$....D$.....$
-
0050 e8fcffff ff8b4424 18c9c3 ......D$...
-
Contents of section .data:
-
0000 54000000 00000000 55000000 T.......U...
-
Contents of section .rodata:
-
0000 68656c6c 6f20776f 726c6421 0025640a hello world!.%d.
-
0010 00323232 323200 .22222.
-
-
-
Disassembly of section .text:
-
-
00000000 <func1>:
-
0: 55 push %ebp
-
1: 89 e5 mov %esp,%ebp
-
3: 83 ec 18 sub $0x18,%esp
-
6: 8b 45 08 mov 0x8(%ebp),%eax
-
9: 89 44 24 04 mov %eax,0x4(%esp)
-
d: c7 04 24 0d 00 00 00 movl $0xd,(%esp)
-
14: e8 fc ff ff ff call 15 <func1+0x15>
-
19: c9 leave
-
1a: c3 ret
-
-
0000001b <main>:
-
1b: 55 push %ebp
-
1c: 89 e5 mov %esp,%ebp
-
1e: 83 e4 f0 and $0xfffffff0,%esp
-
21: 83 ec 20 sub $0x20,%esp
-
24: c7 44 24 14 11 00 00 movl $0x11,0x14(%esp)
-
2b: 00
-
2c: c7 44 24 18 01 00 00 movl $0x1,0x18(%esp)
-
33: 00
-
34: 8b 15 08 00 00 00 mov 0x8,%edx
-
3a: a1 00 00 00 00 mov 0x0,%eax
-
3f: 01 c2 add %eax,%edx
-
41: 8b 44 24 18 mov 0x18(%esp),%eax
-
45: 01 c2 add %eax,%edx
-
47: 8b 44 24 1c mov 0x1c(%esp),%eax
-
4b: 01 d0 add %edx,%eax
-
4d: 89 04 24 mov %eax,(%esp)
-
50: e8 fc ff ff ff call 51 <main+0x36>
-
55: 8b 44 24 18 mov 0x18(%esp),%eax
-
59: c9 leave
-
5a: c3 ret
我们先来看一下.data段里数据:
-
Contents of section .data:
-
0000 54000000 00000000 55000000 T.......U...
因为已初始化的全局变量和已初始化的局部静态变量经常放在.data段里,因为偶的CPUX86是小端,低字节放低位,54000000转化十进制为84,55000000转化为十进制为85,刚好对应代码中的global_init_var
= 84
和static_var
= 85
。
然后我们来看一下.rodata段的数据:
-
Contents of section .rodata:
-
0000 68656c6c 6f20776f 726c6421 0025640a hello world!.%d.
-
0010 00323232 323200 .22222.
.rodata里面你可以看到有个数据,分别为“hello world!”、“%d\n”、"22222",这三个数据分别对应了代码中的三个字符串常量。所以
字符串常量一般放在“.rodata”段里。
接下来就是代码段.text:
-
Contents of section .text:
-
0000 5589e583 ec188b45 08894424 04c70424 U......E..D$...$
-
0010 0d000000 e8fcffff ffc9c355 89e583e4 ...........U....
-
0020 f083ec20 c7442414 11000000 c7442418 ... .D$......D$.
-
0030 01000000 8b150800 0000a100 00000001 ................
-
0040 c28b4424 1801c28b 44241c01 d0890424 ..D$....D$.....$
-
0050 e8fcffff ff8b4424 18c9c3 ......D$...
看到.text段中的两个以"55 89 e5 83 ec ec 18 8b 45"和"89 e5 83 e4 f0 83 ec 20"开头的数据。分别对应汇编代码编译以后的机器指令(十六进制数据相同),见如下:
-
Disassembly of section .text:
-
-
00000000 <func1>:
-
0: 55 push %ebp
-
1: 89 e5 mov %esp,%ebp
-
3: 83 ec 18 sub $0x18,%esp
-
6: 8b 45 08 mov 0x8(%ebp),%eax
-
9: 89 44 24 04 mov %eax,0x4(%esp)
-
d: c7 04 24 0d 00 00 00 movl $0xd,(%esp)
-
14: e8 fc ff ff ff call 15 <func1+0x15>
-
19: c9 leave
-
1a: c3 ret
-
-
0000001b <main>:
-
1b: 55 push %ebp
-
1c: 89 e5 mov %esp,%ebp
-
1e: 83 e4 f0 and $0xfffffff0,%esp
-
21: 83 ec 20 sub $0x20,%esp
-
24: c7 44 24 14 11 00 00 movl $0x11,0x14(%esp)
-
2b: 00
-
2c: c7 44 24 18 01 00 00 movl $0x1,0x18(%esp)
-
33: 00
-
34: 8b 15 08 00 00 00 mov 0x8,%edx
-
3a: a1 00 00 00 00 mov 0x0,%eax
-
3f: 01 c2 add %eax,%edx
-
41: 8b 44 24 18 mov 0x18(%esp),%eax
-
45: 01 c2 add %eax,%edx
-
47: 8b 44 24 1c mov 0x1c(%esp),%eax
-
4b: 01 d0 add %edx,%eax
-
4d: 89 04 24 mov %eax,(%esp)
-
50: e8 fc ff ff ff call 51 <main+0x36>
-
55: 8b 44 24 18 mov 0x18(%esp),%eax
-
59: c9 leave
-
5a: c3 ret
所以说
代码编译后的机器指令经常被放在代码段里。
再看一下.bss段,输入命令:
查看:
-
Sections:
-
Idx Name Size VMA LMA File off Algn
-
2 .bss 00000004 00000000 00000000 0000009c 2**2
-
ALLOC
看到.bss的大小为4,《程序员自我修养》上说只有static_var2存放到.bss段,而global_uninit_var只是一个未定义的“COMMON符号“没有放在任何段里,这是跟不同的语言与不同的编译器实现有关。看完书后在来补充吧。
最后,说bbs段在文件中不不占用空间,请参考下面代码:
1.
-
#include <stdio.h>
-
-
int main(void)
-
{
-
return 0;
-
}
编译查看大小:
-
root@women:/usr/local/src# gcc -c 1.c
-
root@women:/usr/local/src# size 1.o
text data bss dec hex filename
66 0 0 66 42 2.o
-
root@women:/usr/local/src# ls -l 1.o
-
-rw-r--r-- 1 root root 852 8月 27 11:03 2.o
2.比上面代码多了16字节的”
int a
[10
] = {0
};“
-
#include <stdio.h>
-
-
int a[10] = {0};
-
-
int main(void)
-
{
-
return 0;
-
}
再来编译查看大小:
-
root@women:/usr/local/src# gcc -c 2.c
-
root@women:/usr/local/src# ll 2.o
-
-rw-r--r-- 1 root root 868 8月 27 11:13 2.o
-
root@women:/usr/local/src# size 2.o
-
text data bss dec hex filename
-
66 0 40 106 6a 2.o
两段代码便以后,BSS段大小发生了变化多了40个字节,但是实际文件大小只相差16个字节,刚好就是加入代码的”
int a
[10
] = {0
};“这十六个字节。所以说bbs段在文件中不不占用空间。
阅读(967) | 评论(0) | 转发(0) |