bss sectioin观点

    技术2022-05-11  66

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

    #include <stdio.h> int foo; int bar; int main(void) {    int *ptr;    printf(".bss section starts at p/n", &foo);    printf("foo is %d./n", foo);    ptr = &foo;    *ptr = 12345;    printf("foo is %d./n", foo);    printf(".bss section starts at p/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 结构

    由图可以知道,范例的 .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,(

    转载请注明原文地址: https://ibbs.8miu.com/read-15700.html

    最新回复(0)