昨天看《C專家編程》,其中第六章第二節(jié)給出一個(gè)編程挑戰(zhàn),本來覺得是很容易的問題,結(jié)果結(jié)果出乎我的意料。廢話不多說,先看題目
查看可執(zhí)行文件終的段
- 編譯“Hello world”程序,在可執(zhí)行文件終執(zhí)行l(wèi)s -l,得到文件的總體大小。運(yùn)行size得到各段的大小。
- 增加一個(gè)全局的int[1000]數(shù)組聲明,重新編譯,再用上面的命令得到總體及各個(gè)段的大小,注意前后的區(qū)別。
- 現(xiàn)在,在數(shù)組的聲明中增加初始值。這將使數(shù)組從BSS段轉(zhuǎn)到數(shù)據(jù)段。重復(fù)上面的測量,注意各段前后大小的變化。
按照我之前的了解,2的BSS段會(huì)比1多4×1000=4000字節(jié),3的DATA段比1和2的多4000字節(jié)。而實(shí)際情況呢?讓我們用實(shí)驗(yàn)來看看
hello world 1
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}

可以看出:
| text | data | bss |
|---|---|---|
| 1137 | 280 | 4 |
hello world 2
#include <stdio.h>
int a[1000];
int main(void)
{
printf("Hello World!\n");
return 0;
}

可以看出:
| text | data | bss |
|---|---|---|
| 1137 | 280 | 4032 |
這里BSS大小為4032,比剛開始想的還多出了28個(gè)字節(jié),這是怎么回事?
hello world 3
#include <stdio.h>
int a[1000]={1};
int main(void)
{
printf("Hello World!\n");
return 0;
}

可以看出:
| text | data | bss |
|---|---|---|
| 1137 | 4304 | 4 |
這里DATA大小為4304,4304-280=4024,比剛開始想的還多出了24個(gè)字節(jié),這又是怎么回事?
分析
這里我把三個(gè)目標(biāo)文件用nm程序打印出詳細(xì)的段信息。
yiltoncent@yiltoncent-GA-MA785GM-US2H:~/ctest/expert_c_programming_deep_c_secrets$ nm -s ch6_2_1.o
0804a020 B __bss_start
0804a020 b completed.7181
0804a018 D __data_start
0804a018 W data_start
08048360 t deregister_tm_clones
080483d0 t __do_global_dtors_aux
08049f0c t __do_global_dtors_aux_fini_array_entry
0804a01c D __dso_handle
08049f14 d _DYNAMIC
0804a020 D _edata
0804a024 B _end
080484b4 T _fini
080484c8 R _fp_hw
080483f0 t frame_dummy
08049f08 t __frame_dummy_init_array_entry
080485d4 r __FRAME_END__
0804a000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
080482b0 T _init
08049f0c t __init_array_end
08049f08 t __init_array_start
080484cc R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
08049f10 d __JCR_END__
08049f10 d __JCR_LIST__
w _Jv_RegisterClasses
080484b0 T __libc_csu_fini
08048450 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804841b T main
U puts@@GLIBC_2.0
08048390 t register_tm_clones
08048320 T _start
0804a020 D __TMC_END__
08048350 T __x86.get_pc_thunk.bx
yiltoncent@yiltoncent-GA-MA785GM-US2H:~/ctest/expert_c_programming_deep_c_secrets$ nm -s ch6_2_2.o
0804a040 B a
0804a020 B __bss_start
0804a020 b completed.7181
0804a018 D __data_start
0804a018 W data_start
08048360 t deregister_tm_clones
080483d0 t __do_global_dtors_aux
08049f0c t __do_global_dtors_aux_fini_array_entry
0804a01c D __dso_handle
08049f14 d _DYNAMIC
0804a020 D _edata
0804afe0 B _end
080484b4 T _fini
080484c8 R _fp_hw
080483f0 t frame_dummy
08049f08 t __frame_dummy_init_array_entry
080485d4 r __FRAME_END__
0804a000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
080482b0 T _init
08049f0c t __init_array_end
08049f08 t __init_array_start
080484cc R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
08049f10 d __JCR_END__
08049f10 d __JCR_LIST__
w _Jv_RegisterClasses
080484b0 T __libc_csu_fini
08048450 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804841b T main
U puts@@GLIBC_2.0
08048390 t register_tm_clones
08048320 T _start
0804a020 D __TMC_END__
08048350 T __x86.get_pc_thunk.bx
yiltoncent@yiltoncent-GA-MA785GM-US2H:~/ctest/expert_c_programming_deep_c_secrets$ nm -s ch6_2_3.o
0804a040 D a
0804afe0 B __bss_start
0804afe0 b completed.7181
0804a020 D __data_start
0804a020 W data_start
08048360 t deregister_tm_clones
080483d0 t __do_global_dtors_aux
08049f0c t __do_global_dtors_aux_fini_array_entry
0804a024 D __dso_handle
08049f14 d _DYNAMIC
0804afe0 D _edata
0804afe4 B _end
080484b4 T _fini
080484c8 R _fp_hw
080483f0 t frame_dummy
08049f08 t __frame_dummy_init_array_entry
080485d4 r __FRAME_END__
0804a000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
080482b0 T _init
08049f0c t __init_array_end
08049f08 t __init_array_start
080484cc R _IO_stdin_used
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
08049f10 d __JCR_END__
08049f10 d __JCR_LIST__
w _Jv_RegisterClasses 080484b0 T __libc_csu_fini
08048450 T __libc_csu_init
U __libc_start_main@@GLIBC_2.0
0804841b T main
U puts@@GLIBC_2.0
08048390 t register_tm_clones
08048320 T _start
0804afe0 D __TMC_END__
08048350 T __x86.get_pc_thunk.bx
對(duì)比1和2,我們關(guān)注BSS段,注意上面每一行的中間,B就表示BSS段。
如下,對(duì)于1,注意__bss_start和_end的地址。
yiltoncent@yiltoncent-GA-MA785GM-US2H:~/ctest/expert_c_programming_deep_c_secrets$ nm -s ch6_2_1.o
0804a020 B __bss_start
0804a020 b completed.7181
0804a018 D __data_start
0804a018 W data_start
08048360 t deregister_tm_clones
080483d0 t __do_global_dtors_aux
08049f0c t __do_global_dtors_aux_fini_array_entry
0804a01c D __dso_handle
08049f14 d _DYNAMIC
0804a020 D _edata
0804a024 B _end
計(jì)算0x804a024 - 0x804a020 = 4,正好是BSS的大小。
如下,對(duì)于2,注意__bss_start和_end的地址。
yiltoncent@yiltoncent-GA-MA785GM-US2H:~/ctest/expert_c_programming_deep_c_secrets$ nm -s ch6_2_2.o
0804a040 B a
0804a020 B __bss_start
0804a020 b completed.7181
0804a018 D __data_start
0804a018 W data_start
08048360 t deregister_tm_clones
080483d0 t __do_global_dtors_aux
08049f0c t __do_global_dtors_aux_fini_array_entry
0804a01c D __dso_handle
08049f14 d _DYNAMIC
0804a020 D _edata
0804afe0 B _end
計(jì)算0804afe0 - 0804a020 = 0xfc0 = 4032,也正好是BSS的大小。同時(shí)請(qǐng)注意上面另外一個(gè)符號(hào)a,其地址是0x0804a040,與__bss_start相差大小正好為0x20 = 32個(gè)字節(jié)。編譯輸出為何要預(yù)留32個(gè)字節(jié),用來干什么?
最終解決
最終問題搞清楚還是在《程序員的自我修養(yǎng)》的啟示下,里面第三章第三節(jié)使用objdump工具分析目標(biāo)文件。因此我也對(duì)1和2進(jìn)行了分析。
ch6_2_1.o: 文件格式 elf32-i386
節(jié):
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048154 08048154 00000154 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 08048168 08048168 00000168 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 08048188 08048188 00000188 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 00000020 080481ac 080481ac 000001ac 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000050 080481cc 080481cc 000001cc 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000004a 0804821c 0804821c 0000021c 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 0000000a 08048266 08048266 00000266 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 08048270 08048270 00000270 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rel.dyn 00000008 08048290 08048290 00000290 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rel.plt 00000018 08048298 08048298 00000298 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 00000023 080482b0 080482b0 000002b0 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000040 080482e0 080482e0 000002e0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .text 00000192 08048320 08048320 00000320 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini 00000014 080484b4 080484b4 000004b4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata 00000015 080484c8 080484c8 000004c8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .eh_frame_hdr 0000002c 080484e0 080484e0 000004e0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame 000000cc 0804850c 0804850c 0000050c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .init_array 00000004 08049f08 08049f08 00000f08 2**2
CONTENTS, ALLOC, LOAD, DATA
18 .fini_array 00000004 08049f0c 08049f0c 00000f0c 2**2
CONTENTS, ALLOC, LOAD, DATA
19 .jcr 00000004 08049f10 08049f10 00000f10 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .dynamic 000000e8 08049f14 08049f14 00000f14 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000004 08049ffc 08049ffc 00000ffc 2**2
CONTENTS, ALLOC, LOAD, DATA
22 .got.plt 00000018 0804a000 0804a000 00001000 2**2
CONTENTS, ALLOC, LOAD, DATA
23 .data 00000008 0804a018 0804a018 00001018 2**2
CONTENTS, ALLOC, LOAD, DATA
24 .bss 00000004 0804a020 0804a020 00001020 2**0
ALLOC
25 .comment 00000052 00000000 00000000 00001020 2**0
CONTENTS, READONLY
ch6_2_2.o: 文件格式 elf32-i386
節(jié):
Idx Name Size VMA LMA File off Algn
0 .interp 00000013 08048154 08048154 00000154 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .note.ABI-tag 00000020 08048168 08048168 00000168 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .note.gnu.build-id 00000024 08048188 08048188 00000188 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .gnu.hash 00000020 080481ac 080481ac 000001ac 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .dynsym 00000050 080481cc 080481cc 000001cc 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
5 .dynstr 0000004a 0804821c 0804821c 0000021c 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .gnu.version 0000000a 08048266 08048266 00000266 2**1
CONTENTS, ALLOC, LOAD, READONLY, DATA
7 .gnu.version_r 00000020 08048270 08048270 00000270 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .rel.dyn 00000008 08048290 08048290 00000290 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
9 .rel.plt 00000018 08048298 08048298 00000298 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
10 .init 00000023 080482b0 080482b0 000002b0 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
11 .plt 00000040 080482e0 080482e0 000002e0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
12 .text 00000192 08048320 08048320 00000320 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .fini 00000014 080484b4 080484b4 000004b4 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .rodata 00000015 080484c8 080484c8 000004c8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .eh_frame_hdr 0000002c 080484e0 080484e0 000004e0 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
16 .eh_frame 000000cc 0804850c 0804850c 0000050c 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .init_array 00000004 08049f08 08049f08 00000f08 2**2
CONTENTS, ALLOC, LOAD, DATA
18 .fini_array 00000004 08049f0c 08049f0c 00000f0c 2**2
CONTENTS, ALLOC, LOAD, DATA
19 .jcr 00000004 08049f10 08049f10 00000f10 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .dynamic 000000e8 08049f14 08049f14 00000f14 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000004 08049ffc 08049ffc 00000ffc 2**2
CONTENTS, ALLOC, LOAD, DATA
22 .got.plt 00000018 0804a000 0804a000 00001000 2**2
CONTENTS, ALLOC, LOAD, DATA
23 .data 00000008 0804a018 0804a018 00001018 2**2
CONTENTS, ALLOC, LOAD, DATA
24 .bss 00000fc0 0804a020 0804a020 00001020 2**5
ALLOC
25 .comment 00000052 00000000 00000000 00001020 2**0
CONTENTS, READONLY
注意第53行和110行。對(duì)于1來說,BSS段本來就沒有,只是留一個(gè)符號(hào),占用四個(gè)字節(jié),對(duì)齊方式是2^0 = 1。而對(duì)于2來說,BSS段里面有4000字節(jié)的大小,加上本來就有的4個(gè)字節(jié),就是4004字節(jié)大小。但其對(duì)齊方式是2^5 = 32,所以4004字節(jié)被擴(kuò)展成4032個(gè)字節(jié)以滿足對(duì)齊條件。究其原因?yàn)楹问?2字節(jié)對(duì)齊,應(yīng)該跟CPU的緩存機(jī)制有關(guān)吧。
關(guān)于DATA段的討論,以后再繼續(xù),但上述思路應(yīng)該是沒問題的,可以參考借鑒。