Перейти к содержанию

Секции .data и .bss

Глобальные и статические переменные прошивки компоновщик раскладывает по секциям. Переменные с начальным значением попадают в .data: значения хранятся во флеш-памяти и при старте копируются в ОЗУ. Нулевые и неинициализированные — в .bss, которую код запуска просто обнуляет.

Обновлено Редакция GAW

Секция .bss — область ОЗУ для глобальных и статических переменных без инициализатора или с нулевым значением; код запуска заполняет её нулями. Рядом с ней всегда упоминают .data — область для переменных с ненулевыми начальными значениями, которые хранятся во флеш-памяти и при старте копируются в ОЗУ. Распределение по секциям выполняют компилятор и компоновщик, а описывает его скрипт компоновщика.

Переменная в CСекцияФлешОЗУ
int a = 5;.dataда (начальное значение)да
int b; или int b = 0;.bssнетда
const int c = 5;.rodataдаобычно нет
__attribute__((section(".noinit"))) int d;.noinit — если секция есть в скрипте компоновщиканетда, не обнуляется

У классических AVR (ATmega328P и подобных) .rodata по умолчанию лежит внутри .data, в ОЗУ, если константы не размещены во флеш-памяти через PROGMEM. У новых AVR это не так: у семейства avrxmega3 (ATmega4809, tiny 0/1-series) флеш-память видна в адресном пространстве данных, и .rodata остаётся во флеш-памяти; у AVR Dx с FLMAP — тоже, если не задан -mrodata-in-ram (скрипт ld для AVR, avr-libc).

Как это работает при старте

В шаблоне ST для STM32F103 секция .data записана как >RAM AT> FLASH: адрес выполнения в ОЗУ, адрес загрузки во флеш-памяти, его отдаёт символ _sidata. Reset_Handler сначала копирует .data из флеш-памяти в ОЗУ словами, затем обнуляет всё между _sbss и _ebss и только потом вызывает main. У AVR то же делает секция .init4 кода запуска avr-libc. Подробно — в термине «Код запуска».

Где смотреть размер

Утилита size показывает text, data и bss. Флеш-память занимают text + data, ОЗУ — data + bss, а остаток ОЗУ делят куча и стек. Поэтому большой массив, объявленный глобально без инициализатора, не увеличивает файл прошивки, но может не оставить места стеку.

Не путать .bss с кучей: размер .bss известен при компоновке, а куча выделяется во время работы. Форматы файлов прошивки и что в них попадает — в статье «Файлы прошивки».

Частые вопросы

Почему .data занимает и флеш, и ОЗУ?

Начальные значения надо где-то хранить при выключенном питании — они лежат во флеш-памяти, а работает программа с копией в ОЗУ. В скрипте ST это записано как .data >RAM AT> FLASH: адрес выполнения в ОЗУ, адрес загрузки во флеш. Поэтому переменная int x = 5 тратит и флеш, и ОЗУ, а int y без инициализатора — только ОЗУ.

Что попадает в .bss?

Глобальные и статические переменные без инициализатора и с нулевым инициализатором. Хранить нули во флеш-памяти незачем: код запуска просто заполняет область .bss нулями перед вызовом main. Поэтому большой нулевой буфер не увеличивает размер прошивки, но съедает ОЗУ.

Зачем секция .noinit?

Переменные в .noinit не обнуляются при старте и переживают программный сброс — например, сброс от сторожевого таймера. В них хранят счётчик перезагрузок или причину сбоя. avr-libc разрешает помещать туда только переменные без инициализатора, атрибутом section(".noinit"). Секция должна быть в скрипте компоновщика: в стандартном скрипте avr-libc она есть, а в шаблоне ST для STM32F103 её нет, и её добавляют сами — незагружаемой (NOLOAD) областью в ОЗУ.

Источники

  1. STMicroelectronics. cmsis-device-f1, коммит c8e9a4a: Source/Templates/gcc/linker/STM32F103XB_FLASH.ld и Source/Templates/gcc/startup_stm32f103xb.s — _sidata = LOADADDR(.data); .data >RAM AT> FLASH с символами _sdata, _edata; .bss с _sbss, COMMON, _ebss; Reset_Handler копирует .data словами и обнуляет .bss до вызова main
  2. AVR-LibC 2.3.2: Memory Sections (doc/api/sections.dox), Memory Areas and Using malloc() (malloc.dox), crt1/gcrt1.S, avr/iom328p.h, util/ram-usage.h — секция .init4 инициализирует .data и .bss; .noinit — только для переменных без инициализатора, через __attribute__((section(".noinit"))); символы __data_load_start, __bss_start, __heap_start
  3. GNU ld 2.47 Documentation: Input Section Common, Output Section LMA — COMMON-символы помещают в .bss; адрес загрузки (LMA) и адрес выполнения (VMA) секции, LOADADDR
  4. GCC 16.2 Manual: Common Attributes (section, noinit) — атрибут section — только для глобальных объектов; атрибут noinit полагается на секцию .noinit в скрипте компоновщика

Нашли ошибку?