Разница между разделом данных и секцией bss в C

При проверке разборки объектного файла через 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 байтов? Также, когда я разбираю объектный файл,

EDITED :

  [ 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 имеют одинаковый адрес смещения. Означает ли это, что родата, данные и bss относятся к одному и тому же адресу? Содержат ли раздел "Данные", "Родата" и "BSS" значения данных в одном и том же адресе, если да, то как отличить раздел "Данные", "BSS" и "Родата"?

Ответ 1

Секция .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-байтовый g_myGlobal и этот 1-байтовый 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, ее настоящее имя - completed и, вероятно, она является статической внутри некоторой функции. Глядя на crtstuff.c, мы находим виновника: static _Bool completed внутри __do_global_dtors_aux().

Ответ 2

По определению сегмент 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)