34

通过readelf检查目标文件的反汇编时,我看到数据和bss段包含相同的偏移地址。数据部分将包含初始化的全局和静态变量。BSS 将包含未初始化的全局和静态变量。

  1 #include<stdio.h>
  2 
  3 static void display(int i, int* ptr);
  4 
  5 int main(){
  6  int x = 5;
  7  int* xptr = &x;
  8  printf("\n In main() program! \n");
  9  printf("\n x address : 0x%x x value : %d  \n",(unsigned int)&x,x);
 10  printf("\n xptr points to : 0x%x xptr value : %d \n",(unsigned int)xptr,*xptr);
 11  display(x,xptr);
 12  return 0;
 13 }
 14 
 15 void display(int y,int* yptr){
 16  char var[7] = "ABCDEF";
 17  printf("\n In display() function  \n");
 18  printf("\n y value : %d y address : 0x%x  \n",y,(unsigned int)&y);
 19  printf("\n yptr points to : 0x%x yptr value : %d  \n",(unsigned int)yptr,*yptr);
 20 }

输出:

   SSS:~$ size a.out 
   text    data     bss     dec     hex filename
   1311     260       8    1579     62b a.out

在上面的程序中,我没有任何未初始化的数据,但 BSS 占用了 8 个字节。为什么它占用8个字节?另外,当我反汇编目标文件时,

编辑:

  [ 3] .data             PROGBITS        00000000 000110 000000 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 000110 000000 00  WA  0   0  4
  [ 5] .rodata           PROGBITS        00000000 000110 0000cf 00   A  0   0  4

data、rodata 和 bss 具有相同的偏移地址。是不是表示rodata、data、bss指的是同一个地址?Data section、rodata section和bss section是否包含相同地址的数据值,如果是,如何区分data section、bss section和rodata section?

4

2 回答 2

68

当程序加载到内存中时,该.bss部分保证全为零。因此,任何未初始化或初始化为零的全局数据都放置在该.bss部分中。例如:

static int g_myGlobal = 0;     // <--- in .bss section

关于这一点的好处是,.bss节数据不必包含在磁盘上的 ELF 文件中(即,该.bss节的文件中没有整个零区域)。相反,加载程序从节标题中知道要为该.bss节分配多少,并在将控制权交给您的程序之前将其清零。

注意readelf输出:

[ 3] .data PROGBITS 00000000 000110 000000 00 WA 0 0 4
[ 4] .bss NOBITS 00000000 000110 000000 00 WA 0 0 4

.data被标记为PROGBITS。这意味着 ELF 文件中存在加载程序需要为您读取到内存中的程序数据“位”。.bss另一方面被标记NOBITS,这意味着文件中没有任何内容需要作为加载的一部分读入内存。


例子:

// bss.c
static int g_myGlobal = 0;

int main(int argc, char** argv)
{
   return 0;
}

编译它$ gcc -m32 -Xlinker -Map=bss.map -o bss bss.c

查看部分标题$ readelf -S bss

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
   :
  [13] .text             PROGBITS        080482d0 0002d0 000174 00  AX  0   0 16
   :
  [24] .data             PROGBITS        0804964c 00064c 000004 00  WA  0   0  4
  [25] .bss              NOBITS          08049650 000650 000008 00  WA  0   0  4
   :

现在我们在符号表中查找我们的变量:$ readelf -s bss | grep g_myGlobal

37: 08049654     4 OBJECT  LOCAL  DEFAULT   25 g_myGlobal

请注意,g_myGlobal它显示为第 25 节的一部分。如果我们回顾节标题,我们会看到 25 是.bss


要回答您的真正问题:

在上面的程序中,我没有任何未初始化的数据,但 BSS 占用了 8 个字节。为什么它占用8个字节?

继续我的示例,我们在第 25 节中查找任何符号:

$ readelf -s bss | grep 25
     9: 0804825c     0 SECTION LOCAL  DEFAULT    9 
    25: 08049650     0 SECTION LOCAL  DEFAULT   25 
    32: 08049650     1 OBJECT  LOCAL  DEFAULT   25 completed.5745
    37: 08049654     4 OBJECT  LOCAL  DEFAULT   25 g_myGlobal

第三列是大小。我们看到了我们预期的 4-byteg_myGlobal和这个 1-byte completed.5745。这可能是 C 运行时初始化中某处的函数静态变量 - 请记住,很多“东西”发生在main()被调用之前。

4+1=5 个字节。但是,如果我们回头看.bss节头,我们会看到最后一列Al是 4。这就是节对齐,这意味着这个节在加载时将始终是 4 个字节的倍数。从 5 开始的下一个倍数是 8,这就是该.bss部分是 8 个字节的原因。


此外,我们可以查看链接器生成的映射文件,以查看哪些目标文件放置在最终输出中的位置。

.bss            0x0000000008049650        0x8
 *(.dynbss)
 .dynbss        0x0000000000000000        0x0 /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib/crt1.o
 *(.bss .bss.* .gnu.linkonce.b.*)
 .bss           0x0000000008049650        0x0 /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib/crt1.o
 .bss           0x0000000008049650        0x0 /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib/crti.o
 .bss           0x0000000008049650        0x1 /usr/lib/gcc/x86_64-redhat-linux/4.7.2/32/crtbegin.o
 .bss           0x0000000008049654        0x4 /tmp/ccKF6q1g.o
 .bss           0x0000000008049658        0x0 /usr/lib/libc_nonshared.a(elf-init.oS)
 .bss           0x0000000008049658        0x0 /usr/lib/gcc/x86_64-redhat-linux/4.7.2/32/crtend.o
 .bss           0x0000000008049658        0x0 /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib/crtn.o

同样,第三列是大小。

我们看到 4 个字节.bss来自/tmp/ccKF6q1g.o. 在这个简单的例子中,我们知道这是编译我们的 bss.c 文件的临时目标文件。其他 1 个字节来自crtbegin.o,它是 C 运行时的一部分。


最后,由于我们知道这个 1 字节的神秘 bss 变量来自crtbegin.o,并且它被命名为completed.xxxx,所以它的真实名称 iscompleted并且它可能是某个函数内部的静态变量。看着crtstuff.c我们发现了罪魁祸首static _Bool completed__do_global_dtors_aux().

于 2013-05-15T05:50:27.683 回答
4

根据定义,bss段在内存中的某个位置(当程序启动时)但不需要任何磁盘空间。你需要定义一些变量来填充它,所以试试

int bigvar_in_bss[16300];
int var_in_data[5] = {1,2,3,4,5};

您的简单程序中可能没有任何数据.bss,而共享库(如libc.so)可能有“自己的.bss

文件偏移量和内存地址不容易关联。

阅读有关ELF规范的更多信息,也可以使用/proc/(例如,将显示运行该命令cat /proc/self/maps的进程的地址空间)。cat另请阅读proc(5)

于 2013-05-15T05:44:30.777 回答