Сегмент .bss
Сегмент .bss — это область памяти в исполняемых файлах и объектных модулях, предназначенная для хранения глобальных и статических переменных, которые инициализируются нулевыми значениями или не имеют явной инициализации. Название происходит от англ. Block Started by Symbol (блок, начинаемый символом) или, по другой версии, от Block Storage Segment (сегмент блочного хранения). Сегмент .bss является частью форматов исполняемых файлов, таких как ELF (Executable and Linkable Format) в Unix-подобных системах, PE (Portable Executable) в Windows и Mach-O в macOS. Его ключевая особенность — он не занимает места в самом файле на диске, а описывает область памяти, которая выделяется и обнуляется при загрузке программы в оперативную память.
История
Понятие сегмента .bss возникло в ранних версиях операционной системы Unix, разработанной в Bell Labs в конце 1960-х — начале 1970-х годов. В первых реализациях ассемблера и компоновщика для PDP-11 использовалась директива .bss, которая позволяла резервировать память для неинициализированных данных, не занимая место в объектном файле. Это было особенно важно для систем с ограниченным дисковым пространством, где каждый байт имел значение.
В формате a.out (первый формат исполняемых файлов Unix) сегмент .bss следовал за сегментом данных (.data) и сегментом текста (.text). При загрузке программы загрузчик выделял память под .bss и заполнял её нулями. С появлением формата ELF в System V Release 4 (1988 год) сегмент .bss стал частью более сложной структуры, но сохранил свою основную функцию. В современных системах, включая Linux, FreeBSD и macOS, .bss по-прежнему используется для оптимизации размера исполняемых файлов.
Устройство и размещение
Сегмент .bss является частью памяти данных программы, которая включает несколько сегментов:
- .text — исполняемый код программы.
- .rodata — константные данные (только для чтения).
- .data — инициализированные глобальные и статические переменные (с ненулевыми значениями).
- .bss — неинициализированные или нулевые глобальные и статические переменные.
- .heap — куча для динамического выделения памяти (выделяется во время выполнения).
- .stack — стек для локальных переменных и вызовов функций.
В формате ELF сегмент .bss описывается в заголовке программы (Program Header) с типом PT_LOAD. Он имеет виртуальный адрес, размер в памяти (p_memsz) и размер в файле (p_filesz), который равен нулю. Это означает, что загрузчик считывает из файла только описание сегмента, а не его содержимое. При загрузке программы ядро операционной системы выделяет физическую память размером p_memsz и обнуляет её.
Пример размещения в памяти
Рассмотрим простую программу на C:
```c int global_var; // .bss (неинициализирована) int global_init = 42; // .data (инициализирована) static int static_var; // .bss (статическая, неинициализирована)
int main() { int local_var; // .stack (локальная) return 0; } ```
После компиляции и компоновки:
global_varиstatic_varпопадут в сегмент .bss.global_init— в сегмент .data.local_var— на стек, который не входит в сегменты .bss или .data.
При запуске программы загрузчик выделяет память под .bss и обнуляет её, поэтому global_var и static_var получают значение 0.
Классификация и виды
Сегмент .bss можно классифицировать по контексту использования:
- В объектных файлах — .bss является частью объектного модуля, создаваемого компилятором. Он содержит символы (переменные), которые будут объединены компоновщиком.
- В исполняемых файлах — .bss представляет собой объединённый сегмент, включающий все неинициализированные глобальные и статические переменные из всех объектных файлов.
- В динамических библиотеках — .bss может присутствовать в разделяемых объектах (.so в Linux, .dll в Windows), но его размер обычно мал, так как глобальные переменные в библиотеках нежелательны.
- В загружаемых модулях ядра — .bss используется аналогично, но для кода, работающего в пространстве ядра.
Особенности и характеристики
Размер на диске и в памяти
Главная особенность сегмента .bss — его размер в файле равен нулю. Это достигается за счёт того, что в исполняемом файле хранится только описание сегмента (адрес и размер), а не его содержимое. Например, если программа объявляет массив int arr[1000000]; (4 МБ), то в файле .bss займёт всего несколько десятков байт на заголовок, а при запуске выделит 4 МБ оперативной памяти, заполненной нулями. Это позволяет экономить дисковое пространство, особенно для программ с большими статическими массивами.
Инициализация нулями
Все переменные в сегменте .bss гарантированно инициализируются нулями при загрузке программы. Это соответствует стандарту языка C (и C++), который требует, чтобы глобальные и статические переменные без явной инициализации имели нулевое значение. Для локальных переменных такая гарантия отсутствует — они содержат мусор, если не инициализированы явно.
Связь с другими сегментами
В некоторых архитектурах и операционных системах сегмент .bss может быть объединён с сегментом .data в один сегмент данных. Например, в формате PE для Windows сегмент .bss называется .bss и следует за .data, но его размер в файле также равен нулю. В Mach-O для macOS сегмент .bss является частью секции __DATA и называется __bss.
Применение и значение
Сегмент .bss широко используется в низкоуровневом программировании, системном программировании и разработке операционных систем. Его основные применения:
- Оптимизация размера исполняемых файлов — позволяет уменьшить размер программы на диске за счёт выноса неинициализированных данных в виртуальную память.
- Управление памятью — загрузчик может выделять память под .bss лениво (lazy allocation), то есть только при первом обращении, что экономит физическую память.
- Встраиваемые системы — в системах с ограниченной памятью (например, микроконтроллеры) сегмент .bss часто размещается в ОЗУ, а его инициализация нулями выполняется в startup-коде перед вызовом
main(). - Ядро Linux — в образе ядра Linux (vmlinux) сегмент .bss содержит глобальные переменные ядра, которые обнуляются при загрузке. Это критично для стабильности системы.
Пример в embedded-системах
В среде разработки для ARM-микроконтроллеров (например, STM32) компоновщик (linker script) явно определяет размещение сегментов .text, .data и .bss. Обычно .bss размещается в ОЗУ, а его инициализация нулями выполняется в коде startup (на ассемблере или C). Это необходимо, так как после сброса микроконтроллера содержимое ОЗУ не определено.
Интересные факты
- В некоторых старых версиях Unix сегмент .bss назывался
.bssи имел фиксированный размер, задаваемый в ассемблерной директиве. - В языке C переменные, объявленные как
staticвнутри функции, также попадают в .bss, если они не инициализированы явно. Это делает их время жизни равным времени выполнения программы. - В современных компиляторах (например, GCC) оптимизатор может перемещать переменные из .bss в .data, если они инициализируются константой, отличной от нуля.
- В некоторых операционных системах, таких как FreeBSD, сегмент .bss может быть защищён от записи (read-only) для повышения безопасности, но это редкость.
- В формате ELF существует также сегмент
.tbss(Thread-Local BSS), предназначенный для хранения неинициализированных локальных переменных потока.
Критика и ограничения
Несмотря на преимущества, сегмент .bss имеет и недостатки:
- Неэффективность при малых размерах — для программ с небольшим количеством глобальных переменных выигрыш в размере файла незначителен, а сложность описания сегмента может быть избыточной.
- Проблемы с безопасностью — обнуление .bss при загрузке может быть уязвимостью, если злоумышленник может изменить содержимое памяти до инициализации (например, при атаке на загрузчик).
- Сложность отладки — переменные в .bss не имеют начального значения в файле, что затрудняет анализ дампов памяти (core dumps), если они не содержат .bss.
- Ограничения в некоторых архитектурах — в системах с Harvard-архитектурой (например, некоторые микроконтроллеры) сегмент .bss требует явного копирования из ПЗУ в ОЗУ, что увеличивает время загрузки.
Источники
- Стивенс, У. Р. «UNIX: взаимодействие процессов». — СПб.: Питер, 2003. — Глава 7: «Память процессов».
- Левин, Дж. «Linkers and Loaders». — Morgan Kaufmann, 2000. — Глава 3: «Object File Formats».
- Документация GNU ld (Linker Scripts) — раздел «Output Section Description».
- Стандарт ELF (System V Application Binary Interface) — Chapter 4: «Object Files».
- Керниган, Б., Ритчи, Д. «Язык программирования C». — 2-е изд. — М.: Вильямс, 2006. — Приложение A: «Спецификация языка».
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →