Ответ 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()
.