Занимают ли переменные, инициализированные нулевым разделом .bss, место в файле elf?

StackOverflow https://stackoverflow.com/questions/610682

  •  03-07-2019
  •  | 
  •  

Вопрос

Если я правильно понимаю, то .bss раздел в файлах ELF используется для выделения места для переменных с нулевой инициализацией.Наша цепочка инструментов создает файлы ELF, отсюда и мой вопрос:делает ли .bss раздел действительно должен содержать все эти нули?Это кажется такой ужасной тратой пространства, что когда, скажем, я выделяю глобальный десятимегабайтный массив, это приводит к получению десяти мегабайт нулей в файле ELF.Что я здесь не так вижу?

Это было полезно?

Решение

Прошло некоторое время с тех пор, как я работал с ELF.Но мне кажется, я все еще помню этот материал.Нет, он физически не содержит этих нулей.Если вы заглянете в заголовок программы ELF-файла, то увидите, что каждый заголовок содержит два числа:Один из них - это размер файла.И еще один - это размер, который имеет раздел при размещении в виртуальной памяти (readelf -l ./a.out):

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000e0 0x000e0 R E 0x4
  INTERP         0x000114 0x08048114 0x08048114 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00454 0x00454 R E 0x1000
  LOAD           0x000454 0x08049454 0x08049454 0x00104 0x61bac RW  0x1000
  DYNAMIC        0x000468 0x08049468 0x08049468 0x000d0 0x000d0 RW  0x4
  NOTE           0x000128 0x08048128 0x08048128 0x00020 0x00020 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

Заголовки типа LOAD это те, которые копируются в виртуальную память, когда файл загружается для выполнения.Другие заголовки содержат другую информацию, например, о необходимых совместно используемых библиотеках.Как вы видите, FileSize и MemSiz существенно отличаются для заголовка, содержащего bss раздел (второй LOAD один):

0x00104 (file-size) 0x61bac (mem-size)

Для этого примера кода:

int a[100000];
int main() { }

Спецификация ELF гласит, что часть сегмента, размер которого больше размера файла, просто заполняется нулями в виртуальной памяти.Сопоставление сегмента с секцией второго LOAD заголовок выглядит следующим образом:

03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss

Так что там есть и несколько других разделов.Для конструкторов / деструкторов C ++.То же самое и для Java.Затем он содержит копию .dynamic раздел и другие материалы, полезные для динамического связывания (я полагаю, что это то место, которое содержит необходимые разделяемые библиотеки среди прочего).После этого .data раздел, содержащий инициализированные глобальные и локальные статические переменные.В конце концов, .bss появляется раздел, который заполняется нулями во время загрузки, поскольку размер файла его не покрывает.

Кстати, вы можете увидеть, в какую секцию вывода будет помещен конкретный символ, используя -M опция компоновщика.Для gcc вы используете -Wl,-M чтобы передать опцию компоновщику.Приведенный выше пример показывает, что a выделяется в пределах .bss.Это может помочь вам убедиться, что ваши неинициализированные объекты действительно попадают в .bss и не где -нибудь еще:

.bss            0x08049560    0x61aa0
 [many input .o files...]
 *(COMMON) 
 *fill*         0x08049568       0x18 00
 COMMON         0x08049580    0x61a80 /tmp/cc2GT6nS.o
                0x08049580                a
                0x080ab000                . = ALIGN ((. != 0x0)?0x4:0x1) 
                0x080ab000                . = ALIGN (0x4) 
                0x080ab000                . = ALIGN (0x4) 
                0x080ab000                _end = .

GCC по умолчанию сохраняет неинициализированные глобальные переменные в ОБЩЕМ разделе для совместимости со старыми компиляторами, которые позволяют определять глобальные переменные дважды в программе без множественных ошибок определения.Использование -fno-common чтобы заставить GCC использовать разделы .bss для объектных файлов (это не имеет значения для конечного связанного исполняемого файла, потому что, как вы видите, он все равно попадет в раздел вывода .bss.Это контролируется скрипт компоновщика.Отобразите это с помощью ld -verbose).Но это не должно вас пугать, это просто внутренняя деталь.Смотрите справочную страницу gcc.

Другие советы

Тот Самый .bss раздел в файле ELF используется для статических данных, которые не инициализирован программно, но гарантированно обнуляется во время выполнения.Вот небольшой пример, который объяснит разницу.

int main() {
    static int bss_test1[100];
    static int bss_test2[100] = {0};
    return 0;
}

В данном случае bss_test1 помещается в .bss поскольку он неинициализирован. bss_test2 однако помещается в .data сегмент вместе с кучей нулей.Загрузчик среды выполнения в основном выделяет объем пространства, зарезервированного для .bss и обнуляет его до начала выполнения любого пользовательского кода.

Вы можете увидеть разницу, используя objdump, nm, или аналогичные утилиты:

moozletoots$ objdump -t a.out | grep bss_test
08049780 l     O .bss   00000190              bss_test1.3
080494c0 l     O .data  00000190              bss_test2.4

Обычно это один из первых сюрпризы с которыми сталкиваются разработчики встраиваемых систем...никогда не инициализируйте статику нулем явно.Загрузчик среды выполнения (обычно) заботится об этом.Как только вы что-либо явно инициализируете, вы сообщаете компилятору / компоновщику включить данные в исполняемый образ.

A .bss раздел не хранится в исполняемом файле.Из наиболее распространенных разделов (.text, .data, .bss), только .text (фактический код) и .data (инициализированные данные) присутствуют в файле ELF.

Это верно, .bss физически отсутствует в файле, скорее, присутствует только информация о его размере, чтобы динамический загрузчик выделил раздел .bss для прикладной программы.Как правило, ЗАГРУЖАЕТСЯ только TLS-сегмент, который получает память для прикладной программы, остальные используются для динамического загрузчика.

Что касается статического исполняемого файла, разделам bss также отводится место в исполняемом файле

Встроенное приложение, в котором нет загрузчика, это обычное дело.

Суман

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top