Миро Самек. Построение простых систем на ARM-контроллерах с использованием инструментов GNU (перевод)


Часть 3

В этой части описывается управляющий файл компоновщика GNU для простого (bare-metal) проекта на процессоре ARM. Рекомендуется прочесть "Embedded System Design on a Shoestring" by Lewin Edwards [1], а именно третью главу "Ld - GNU Linker".


3.1 Управляющий файл компоновщика

Имена секций и специальные символы управляющего файла компоновщика должны соответствовать стартовому коду, описанному в части 2 "2.1 Стартовый код". Управляющий файл не может быть универсальным, так как в нём объявляется конкретная карта распределения памяти целевого кристалла с учётом требований приложения.

Управляющий файл компоновщика, упоминаемый здесь как "blinky.ld", соответствует примеру "Blinky", мигающему четырьмя светодиодами отладочной платы AT91SAM7S-EK. Версия для языка "Си" располагается в каталоге "c_blinky", а для языка C++ - в каталоге "cpp_blinky".

 
Листинг 3.1 Управляющий файл компоновщика для учебного проекта "Blinky" (AT91SAM7S64)

(1)   OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
(2)   OUTPUT_ARCH(arm)
(3)   ENTRY(_vectors)

(4)   MEMORY {
            /* memory map of AT91SAM7S64 */
(5)         ROM (rx)  : ORIGIN = 0x00100000, LENGTH = 64k
(6)         RAM (rwx) : ORIGIN = 0x00200000, LENGTH = 16k
      }

      /* The sizes of the stacks used by the application. */
(7)   C_STACK_SIZE   = 512;
      IRQ_STACK_SIZE = 0;
      FIQ_STACK_SIZE = 0;
      SVC_STACK_SIZE = 0;
      ABT_STACK_SIZE = 0;
      UND_STACK_SIZE = 0;

      /* The size of the heap used by the application. */
      HEAP_SIZE = 0;

(8)   SECTIONS {
(9)         .reset : {
                  /* startup code (ARM vectors and reset handler) */
(10)              *startup.o (.text)
(11)              . = ALIGN(0x4);
(12)        } >ROM

(13)        .ramvect : {
                  /* used for vectors remapped to RAM */
                  __ram_start = .;
(14)              . = 0x40;
(15)        } >RAM

(16)        .fastcode : {
(17)              __fastcode_load = LOADADDR (.fastcode);
(18)              __fastcode_start = .;

(19)              *(.glue_7t) *(.glue_7)
                  *isr.o (.text.*)
(20)              *(.text.fastcode)
(21)              *(.text.Blinky_dispatch)
(22)              /* add other modules here ... */

                  . = ALIGN (4);
                  __fastcode_end = .;
(23)        } >RAM AT>ROM

(24)        .text : {
                  . = ALIGN(4);
                  *(.text)          /* code) */
                  *(.text*)         /* code) */
(27)              *(.rodata)        /* constants, strings, etc. */
                  *(.rodata*)       /* constants, strings, etc. */
(25)              *(.glue_7)        /* NOTE: placed already in .fastcode */
                  *(.glue_7t)       /* NOTE: placed already in .fastcode */

                  KEEP (*(.init))
                  KEEP (*(.fini))

                  . = ALIGN(4);
                  _etext = .;       /* global symbol at end of code */
(28)        } >ROM

(26)        .preinit_array : {
            ...
            .init_array : {
            ...
            .fini_array : {
            ...
            } >ROM

(30)        .data : {
                  __data_load = LOADADDR (.data);
                  __data_start = .;
                  *(.data)          /* .data sections */
                  *(.data*)         /* .data* sections */
                  . = ALIGN(4);
                  _edata = .;
(31)        } >RAM AT>ROM

(32)        .bss : {
                  __bss_start__ = . ;
                  *(.bss)
                  *(.bss*)
                  *(COMMON)
                  . = ALIGN(4);
                  _ebss = .;        /* define a global symbol at bss end */
                  __bss_end__ = .;
(33)        } >RAM

(37)        PROVIDE ( end = _ebss );
            PROVIDE ( _end = _ebss );
            PROVIDE ( __end__ = _ebss );

            .heap : {
                  __heap_start__ = . ;
                  . = . + HEAP_SIZE;
                  . = ALIGN(4);
                  __heap_end__ = . ;
            } >RAM

(34)        .stack : {
                  __stack_start__ = . ;

                  . += IRQ_STACK_SIZE;
                  . = ALIGN (4);
                  __irq_stack_top__ = . ;

                  . += FIQ_STACK_SIZE;
                  ...
                  ...

                  . += C_STACK_SIZE;
                  . = ALIGN (4);
(35)              __c_stack_top__ = . ;

                  __stack_end__ = .;
(36)        } >RAM

            /* Remove information from the standard libraries */
            /DISCARD/ : {
                  libc.a ( * )
                  libm.a ( * )
                  libgcc.a ( * )
            }
      }
										

Выше представлен управляющий файл компоновщика для примера "Blinky". Небольшие отличия версий файла для "Си" и C++ будут объснены позднее. Ниже отмечаются основные особенности.

(1) Директива "OUTPUT_FORMAT" объявляет формат выходного файла (elf32, little-endian, ARM).

(2) Директива "OUTPUT_ARCH" объявляет архитектуру целевой машины.

(3) "ENTRY" указывает конкретную инструкцию [метку] с которой начинается исполняемый код программы.

(4) "MEMORY" описывает размер и расположение блоков памяти целевого контроллера.

(5) Область "ROM" соответствует флэш-памяти кристалла AT91SAM7S64. Она может включать секции "только для чтения" и исполняемые секции (rx), начинается с адреса 0x00100000 и имеет размер 64KB.

(6) Область "RAM" соответствует статической памяти кристалла AT91SAM7S64. Она может включать секции "только для чтения", секции для "чтения-записи" и исполняемые секции (rwx), начинается по адресу 0x00200000 и имеет размер 16KB.

(7) Данные идентификаторы объявляют размеры стеков, которые следует подогнать под требования конкретного приложения. "C-stack" не может иметь нулевой размер.

(8) Команда "SECTIONS" предваряет объявление секций.

(9) Секция ".reset" содержит стартовый код (включая таблицу векторов ARM) и должна быть первой в программной памяти (области ROM).

(10) Данная строка относит к секции ".reset" содержимое всех секций ".text" из файла "startup.o".

(11) Секция должна быть выровнена по границе слова (4 байта).

(12) Секция относится к области "ROM", определённой командой "MEMORY".

(13) Секция ".ramvect" содержит таблицу векторов в оперативной памяти и вторичную таблицу адресов переходов и должна быть первой в оперативной памяти (области RAM).

(14) Размер таблицы векторов и вторичнай таблицы адресов переходов известен - 0x40 байт. Данной инструкцией счётчик адресов секции увеличивается на 0x40 байт, резервируя место под таблицы.

(15) Секция ".ramvect" относится к области "RAM", определённой командой "MEMORY".

(16) Секция ".fastcode" предназначена для кода, исполняемого из оперативной памяти. Располагается он в программной памяти ("ROM"), откуда впоследствии копируется в оперативную память ("RAM"), где и исполняется.

(17) В секции ".fastcode" адрес расположения (load memory address - LMA) отличается от рабочего адреса (virtual memory address - VMA). Идентификатор "__fastcode_load" соответствует LMA в области "ROM" и требуется для стартового кода при копировании секции из программной памяти в оперативную.

(18) Идентификатор "__fastcode_start" соответствует рабочему адресу (VMA) секции ".fastcode" и нужен стартовому коду при копировании секции из программной памяти в оперативную [в данном примере секция ".fastcode" будет находиться в оперативной памяти сразу после обеих таблиц векторов].

(19) Секции ".glue_7t" и ".glue_7" автоматически создаются компилятором когда в коде объявляется переход между наборами машинных инструкций "ARM" и "THUMB". Секции содержат декоративные вызовы между кодом "THUMB" и "ARM" и вызываются при каждом переключении между наборами инструкций. В большинстве случаев выгодно располагать эти маленькие участки "горячего" кода в оперативной памяти.

(20) Код конкретных C/C++ функций явным образом располагается в секции ".text.fastcode" с помощью директивы "__attribute__ ((section (".text.fastcode")))".

(21) Компилятор GNU может располагать каждую функцию в индивидуальной кодовой секции с названием, получаемым добавлением имени функции к имени секции (требуется указать опцию компилятора "-ffunction-sections"). Это позволяет избирательно подходить к расположению кода каждой функции (расположить, например, "Blinky_dispatch()" в оперативной памяти).

Примечание. Компилятор C++ проводит декорирование имён (name-mangling) и для выяснения идентификатора, присвоенного конкретной функции, следует заглянуть в "map"-файл. (Скажем, метод "Blinky::shift()" располагается в секции ".text._ZN6Blinky5shiftEv").

(22) Возможно, в процессе настройки понадобится переместить в оперативную память и другие функции.

(23) Рабочий адрес секции ".fastcode" находится в оперативной памяти, но размещается она в области "ROM".

(24) Секция ".text" предназначена для кода и данных "только для чтения", остающихся на своём месте (в области "ROM"). [адрес размещения (LMA) и рабочий адрес (VMA) совпадают]

(25) При повторном объявлении о размещении кода, уже упомянутого в другой секции (скажем, в ".fastcode"), первое [в порядке появления] объявление имеет преимущество. Но если впоследствии будет решено убрать объявление о размещении из первой секции (здесь - ".fastcode"), заработает второе объявление.

(26) Данные секции создаются компилятором GNU C++ и используются для статических конструкторов и деструкторов.

(27) Секция ".rodata" используется для констант (данных "только для чтения") таких, как таблицы. Так же, как и для кода, для часто используемых констант можно выбирать расположение в оперативной памяти, помещая их в секцию ".fastcode".

(28) Секция ".text" находится и загружается в область "ROM".

(29) [отсутствует] Секция ".ARM.exidx" используется для обработки исключений языка C++ и показана здесь для полноты картины. Простые (Bare-metal) проекты не могут позволить себе избыточный код таких обработчиков.

(30) Секция ".data" содержит инициализированные данные.

(31) Секция ".data" должна находиться в оперативной памяти и копируется из области размещения в "ROM" по рабочему адресу на этапе начальной загрузки.

(32) Секция ".bss" содержит область неинициализированных данных. Стандарты C/C++ требуют, чтобы данная секция была обнулена перед использованием.

(33) Секция ".bss" размещается только в оперативной памяти.

(34) В секции ".stack" располагаются все стеки. На этапе начальной загрузки эта секция заполняется специальным значением.

(35) Инструменты GNU учитывают, что архитектура ARM использует модель стека, растущего к младшим адресам памяти. Поэтому файл компоновщика указывает только адреса верхушек стеков при их инициализации. "Си"-стек (он же стек SYS) располагается в конце секции ".stack".

(36) Секция ".stack" размещается в оперативной памяти.

(37) Идентификаторы "_end", "__end" и "end" используются для указания на начало динамической области памяти, если таковая используется.

(38) [отсутствует] Эта и последующие секции используются отладчиком и не загружаются в целевой контроллер.

ПредпросмотрAttachmentSize
bare_metal_arm_systems_html.zip327.73 КБ
blinky_files.zip173.94 КБ