bss sectioin观点

bss 节区存放「uninitialized data」 ,由程序代码的角度来看,就是「未初始化的变量」。我们直接以一段 code 来说明,让大家更清楚这样的概念。

#include <stdio.h>
  
  


int foo;
int bar;
  
  


int main(void)
{
  
  
   int *ptr;
  
  


   printf(".bss section starts at %08p/n", &foo);


   printf("foo is %d./n", foo);


   ptr = &foo;
   *ptr = 12345;
  
  
 
 


   printf("foo is %d./n", foo);
   printf(".bss section starts at %08p/n", &foo);
  
  
 
 


   return 0;
}
  
  

这段 code 相当简单,但是隐含几个重要的观念,条列说明如下:

1. foo 是一个变量 ,在程序代码里没有被初始化(uninitialized),所以程序执行时(process),foo 变量会被摆在「.bss section」。
2.
同理,bar 变数也是。
3. foo
是第一个 uninitialized data,所以他的 virtual address,形同 .bss section 的开始地址(process virtual address)。

程序要实验的 项目如下:

1. 观念 3. 的应用,我们印出 .bss section start address
2. foo
是全域 变数,未初始化时的值是 0zero)。
3.
'*ptr' 指向 .bss section start address,此地址等于 foo 变量的值。
4.
.bss section 启始地址处内存的值(value)改成 12345(透 过 ptr 指标)。

没搞错的话, foo 变数的值就会变成 12345

以下是执行结果:

# ./bss
  
  
.bss section starts at 0x8049588
  
  
foo is 0.
  
  
foo is 12345.
  
  
.bss section starts at 0x8049588
  
  

很特别的一个 section,值得深入研究。

.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        080480f 4 0000f 4 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          080481a 0 0001a 0 00004c 00    A   0   0  1

  [ 6] .gnu.version      VERSYM          080481ec 0001ec 00000a 02    A   4   0  2

  [ 7] .gnu.version_r    VERNEED         080481f 8 0001f 8 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 0000c 8 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 的「sizefield 就是 12

.bss section 的结构

.bss section 的空间结构类似于 stack,所以前一则日记讲述的「foo 是第一个 uninitialized data,所以他的 virtual address,形同 .bss section 的开始地址(process virtual address)。」观念,并非全然正确

目前已经了解到:.bss section linking view 时是不占档案长度的,在 execution view 时,根据其长度来占用内存大小。

关于 .bss section 的结构,其实一张图就够了。直接切入重点吧!

前言

先重新编译 bss.c 范例:

# gcc -g -o bss bss.c
# ./bss
.bss section starts at 0x8049588
foo is 0.
foo is 12345.
.bss section starts at 0x8049588

依照先前日记的说明,.bss section 的长度为 12 bytes。无论程序是否有 uninitialized dataprocess 一定会有 .bss section,并且 .bss section 的长度至少为 4 bytesIA32),第一笔资料是 "completed.1",该笔数据纪录 .bss section 的起启地址。

另外,在「linker script」里,定义了一个叫 "__bss_start" 的符号,此符号才是纪录 .bss section 的真正起始地址。不过,在此先不讨论这个部份。

Process .bss section 结构

Process .bss section 占用的内存大小,是根据 .bss section 的长度,在执行时期为每一笔 uninitialized data保留下来的。其结构如下图所示,我们以 bss.c 的范例来说明。

bss_section_structure.jpg

bss_section_structure.jpg

.bss section 结构

由图可以知道,范例的 .bss section start address 0x8049584,这是直接查询 .bss section 里的 "completed.1" 符号得知的。'completed.1' address 可利用 nm 查询:

# nm -v bss|grep 'completed.1'
08049584 b completed.1

由此了解,.bss section 真正的 start address 应该是 'completed.1';不过,若把第一笔资料的 start address 当做 .bss section start address 其实也无妨,或者说这是 .bss section「放 data」的 start address

利用 gdb 来观察

# gdb ./bss
...
(gdb) disassemble 0x8049588
Dump of assembler code for function foo:
0x08049588 <foo+0>: add %al,(%eax)
0x 0804958a <foo+2>: add %al,(%eax)

End of assembler dump.
(gdb) disassemble 0x8049584
Dump of assembler code for function completed.1:
0x08049584 <completed.1+0>: add %al,(%eax)
0x08049586 <completed.1+2>: add %al,(%eax)

(gdb) disassemble 0x 804958c
Dump of assembler code for function bar:
0x 0804958c <bar+0>: add %al,(%eax)
0x0804958e <bar+2>: add %al,(%eax)

End of assembler dump.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值