C 中 data 部分和 bss 部分的区别 [英] Difference between data section and the bss section in C

查看:50
本文介绍了C 中 data 部分和 bss 部分的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

When checking the disassembly of the object file through the readelf, I see the data and the bss segments contain the same offset address. The data section will contain the initialised global and static variables. BSS will contain un-initialised global and static variables.

  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个字节?同样,当我反汇编目标文件时,

Here in the above program I don't have any un-initialised data but the BSS has occupied 8 bytes. Why does it occupy 8 bytes? Also when I disassemble the object file,

  [ 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指的是同一个地址?数据段、rodata段和bss段的数据值是否在同一个地址,如果有,如何区分数据段、bss段和rodata段?

data, rodata and bss has the same offset address. Does it mean the rodata, data and bss refer to the same address? Do Data section, rodata section and the bss section contain the data values in the same address, if so how to distinguish the data section, bss section and rodata section?

推荐答案

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

The .bss section is guaranteed to be all zeros when the program is loaded into memory. So any global data that is uninitialized, or initialized to zero is placed in the .bss section. For example:

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

关于这个的好处是,.bss 部分数据不必包含在磁盘上的 ELF 文件中(即文件中没有整个零区域对于 .bss 部分).取而代之的是,加载器从段头中知道要为 .bss 段分配多少空间,并在将控制权移交给您的程序之前将其清零.

The nice part about this is, the .bss section data doesn't have to be included in the ELF file on disk (ie. there isn't a whole region of zeros in the file for the .bss section). Instead, the loader knows from the section headers how much to allocate for the .bss section, and simply zero it out before handing control over to your program.

注意 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,这意味着文件中没有任何内容需要作为加载的一部分读入内存.

.data is marked as PROGBITS. That means there are "bits" of program data in the ELF file that the loader needs to read out into memory for you. .bss on the other hand is marked NOBITS, meaning there's nothing in the file that needs to be read into memory as part of the load.

示例:

// 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

Now we look for our variable in the symbol table: $ readelf -s bss | grep g_myGlobal

37: 08049654     4 OBJECT  LOCAL  DEFAULT   25 g_myGlobal

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

Note that g_myGlobal is shown to be a part of section 25. If we look back in the section headers, we see that 25 is .bss.

回答你真正的问题:

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

Here in the above program I dont have any un-intialised data but the BSS has occupied 8 bytes. Why does it occupy 8 bytes ?

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

Continuing with my example, we look for any symbol in section 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 字节 g_myGlobal,以及这个 1 字节的 completed.5745.这可能是 C 运行时初始化中某处的函数静态变量 - 请记住,在调用 main() 之前会发生很多事情".

The third column is the size. We see our expected 4-byte g_myGlobal, and this 1-byte completed.5745. This is probably a function-static variable from somewhere in the C runtime initialization - remember, a lot of "stuff" happens before main() is ever called.

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

4+1=5 bytes. However, if we look back at the .bss section header, we see the last column Al is 4. That is the section alignment, meaning this section, when loaded, will always be a multiple of 4 bytes. The next multiple up from 5 is 8, and that's why the .bss section is 8 bytes.

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

Additionally We can look at the map file generated by the linker to see what object files got placed where in the final output.

.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

同样,第三列是大小.

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

We see 4 bytes of .bss came from /tmp/ccKF6q1g.o. In this trivial example, we know that is the temporary object file from the compilation of our bss.c file. The other 1 byte came from crtbegin.o, which is part of the C runtime.

最后,既然我们知道这个1字节的神秘bss变量来自crtbegin.o,并且它的名字是completed.xxxx,那么它的真实名字是completed 并且它可能是某个函数内部的静态.查看 crtstuff.c 我们找到了罪魁祸首:__do_global_dtors_aux() 中的一个 static _Bool completed.

Finally, since we know that this 1 byte mystery bss variable is from crtbegin.o, and it's named completed.xxxx, it's real name is completed and it's probably a static inside some function. Looking at crtstuff.c we find the culprit: a static _Bool completed inside of __do_global_dtors_aux().

这篇关于C 中 data 部分和 bss 部分的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆