在「理解 dynamic loader 內部原理的幾個先備知識(一)」講到:.bss 節區「linking view」上不佔檔案空間。這點可以用 readelf 來做 ELF linking view 端的印證:
# readelf -e bss|more (bss 是我們的範例執行檔) ... Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 080480f4 0000f4 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048108 000108 000020 00 A 0 0 4 [ 3] .hash HASH 08048128 000128 000028 04 A 4 0 4 [ 4] .dynsym DYNSYM 08048150 000150 000050 10 A 5 1 4 [ 5] .dynstr STRTAB 080481a0 0001a0 00004c 00 A 0 0 1 [ 6] .gnu.version VERSYM 080481ec 0001ec 00000a 02 A 4 0 2 [ 7] .gnu.version_r VERNEED 080481f8 0001f8 000020 00 A 5 1 4 [ 8] .rel.dyn REL 08048218 000218 000008 08 A 4 0 4 [ 9] .rel.plt REL 08048220 000220 000010 08 A 4 b 4 [10] .init PROGBITS 08048230 000230 000017 00 AX 0 0 4 [11] .plt PROGBITS 08048248 000248 000030 04 AX 0 0 4 [12] .text PROGBITS 08048278 000278 0001b8 00 AX 0 0 4 [13] .fini PROGBITS 08048430 000430 00001b 00 AX 0 0 4 [14] .rodata PROGBITS 0804844c 00044c 000031 00 A 0 0 4 [15] .eh_frame PROGBITS 08048480 000480 000004 00 A 0 0 4 [16] .data PROGBITS 08049484 000484 00000c 00 WA 0 0 4 [17] .dynamic DYNAMIC 08049490 000490 0000c8 08 WA 5 0 4 [18] .ctors PROGBITS 08049558 000558 000008 00 WA 0 0 4 [19] .dtors PROGBITS 08049560 000560 000008 00 WA 0 0 4 [20] .jcr PROGBITS 08049568 000568 000004 00 WA 0 0 4 [21] .got PROGBITS 0804956c 00056c 000018 04 WA 0 0 4 [22] .bss NOBITS 08049584 000584 00000c 00 WA 0 0 4 [23] .comment PROGBITS 00000000 000584 000132 00 0 0 1 ...
重點的部份我用粗體字標示出來了:.bss section 與 .comment section 在檔案裡的 offset 是相同的。不過,用「他人」的工具來印可能會有一些盲點存在,比如說,我們可能不是很明白「Off」真正的意義;建議使用我們自行撰寫的 ELF 讀檔程式 loader-0.5.c()來做,因為這是自己寫的工具,能保證一些盲點都能得到證明。以下是用 loader-0.5.c 印出來的畫面:
# ./loader bss ELF Identification Class: 32-bit objects Machine: Intel 80386 Name Size FileOff [00] .interp 19 244 [01] .note.ABI-tag 32 264 [02] .hash 40 296 [03] .dynsym 80 336 [04] .dynstr 76 416 [05] .gnu.version 10 492 [06] .gnu.version_r 32 504 [07] .rel.dyn 8 536 [08] .rel.plt 16 544 [09] .init 23 560 [10] .plt 48 584 [11] .text 440 632 [12] .fini 27 1072 [13] .rodata 49 1100 [14] .eh_frame 4 1152 [15] .data 12 1156 [16] .dynamic 200 1168 [17] .ctors 8 1368 [18] .dtors 8 1376 [19] .jcr 4 1384 [20] .got 24 1388 [21] .bss 12 1412 [22] .comment 306 1412
了解 ELF 並自己撰寫工具,此過程讓我們了解到「Offset」指的是「確實是該 section 在檔案裡的啟始讀取位置」。這代表,無論程式裡有多少 uninitialized data,都是不佔用額外的檔案空間的。
畫面中的節區大小
「Size」代表該 section 的實體大小(in bytes),以 .bss section 來說,.bss section 的大小是 12 bytes。很不幸的是,這個大小並非表示 .bss section 佔用的「檔案大小」,而是「記憶體大小」;這可能會是一個使用工具時,因為畫面的「字義」所不小心產生的盲點。所以如果把 .bss section 的 Offset 加上他的 Size,並不會等於 .comment section 的 Offset。
所謂的「Size」,包含由 objdump 與 readelf 所列印出來的畫面,或者說,「紀載在 section header entry」裡的 size 資訊,是表示「該 section 的實體記憶體大小」。
.bss section 的長度計算方式
.bss 的大小計算方式為(IA32 平臺):
4 bytes + sizeof(所有的 uninitialized data)
這代表 .bss section 在記憶體所會佔用的長度。以先前的例子來說,計算式會是:
4 + sizeof(foo) + sizeof(bar) = 4 + 4 + 4 = 12 (bytes)
所以,.bss section 的「size」field 就是 12。
.bss section 的結構
.bss section 的空間結構類似於 stack,所以前一則日記講述的「foo 是第一個 uninitialized data,所以他的 virtual address,形同 .bss section 的開始位址(process virtual address)。」觀念,並非全然正確。
此部份留待後續再做說明。