Попытка понять адрес загрузочной памяти (LMA) и смещение двоичного файла в двоичном образе ARM - PullRequest
0 голосов
/ 07 февраля 2019

Я работаю в ARM Cortex M4 ( STM32F4xxxx ) и пытаюсь понять, как именно создаются двоичные файлы (*.elf и *.bin) имелькнул в памяти, особенно в отношении мест памяти.В частности, я не понимаю, как LMA переводится из фактического смещения двоичного файла.Позвольте мне объяснить на примере:

У меня есть файл *.elf, чьи (соответствующие) разделы следующие: (полученный из objdump -h)

my_file.elf:     file format elf32-littlearm

Sections:
Idx Name              Size      VMA       LMA       File off  Algn
  0 .text             000001c4  08010000  08010000  00020000  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .bootloader       00004000  08000000  08000000  00010000  2**0
                      CONTENTS, ALLOC, LOAD, DATA

Согласно этому файлуVMA и LMA - 0x8000000 и 0x8010000, что прекрасно, так как они определены таким образом в файле сценария компоновщика.Кроме того, согласно этому отчету, смещения этих секций равны 0x10000 и 0x20000 соответственно.Затем я выполняю следующую команду для выгрузки памяти, соответствующей .bootloader:

xxd -s 0x10000 -l 16 my_file.elf
00010000: b007 c0de b007 c0de b007 c0de b007 c0de  ................ 

Теперь, создайте двоичный файл, который будет записан в память:

arm-none-eabi-objcopy -O binary --gap-fill 0xFF -S my_file.elf my_file.bin 

Согласноинформация, представленная выше, и, насколько я понимаю, сгенерированный двоичный файл должен иметь раздел .bootloader, расположенный в 0x8000000.Я понимаю, что это не так, как это на самом деле работает, поскольку файл становится очень большим, поэтому bootloader помещается в начале файла, поэтому адрес 0x0 (проверьте, что оба блока памяти идентичны, дажехотя они находятся по разным адресам):

xxd -s 0x00000 -l 16 my_file.bin
00000000: b007 c0de b007 c0de b007 c0de b007 c0de  ................

Насколько я понимаю, когда упомянутый двоичный файл записывается в память, bootloader будет по адресу 0x0, что прекраснос учетом того, что рассматриваемый MCU переходит на адрес 0x4 (после получения SP от 0x0), когда он начинает работать, как я проверил здесь (стр. 26): https://www.st.com/content/ccc/resource/technical/document/application_note/76/f9/c8/10/8a/33/4b/f0/DM00115714.pdf/files/DM00115714.pdf/jcr:content/translations/en.DM00115714.pdf

Наконец, мои вопросы:

Будет ли bootloader фактически размещен в 0x0?Если да, то какова цель определения секторов памяти в файле компоновщика?

Это потому, что 0x0 принадлежит флэш-памяти, и когда MCU запускается, вся флэш-память копируется в RAM по адресу 0x8000000?Если это так, будет ли bootloader выполняться из флэш-памяти и всего остального кода из RAM?

Если принять во внимание вышеупомянутые вопросы, если я ничего не понял, какова связь / различиемежду LMA и File offset?

Ответы [ 2 ]

0 голосов
/ 08 февраля 2019

В документации довольно ясно указано, где находится адресное пространство для кода приложения для stm32, равного 0x08000000 (конкурирующий поставщик похож на 0x01000000 и т. Д.).И что при загрузке в определенном режиме 0x08000000 сопоставляется с адресом 0x00000000, что легко увидеть с помощью отладчика (в обоих пространствах).

Адресное пространство в 0x00000000, сопоставленное с 0x08000000, меньше, чем потенциальное адресное пространствов 0x08000000 в зависимости от чипа.Поэтому целесообразно создавать и использовать 0x08000000, а не 0x00000000, но для небольших программ вы можете выбрать любой из них.

Поскольку cortex-m является машиной векторной таблицы, когда логика считывает адрес 0x00000004, который отображается на 0x08000004 вв нормальном режиме загрузки он видит 0x080xxxxx, а затем выходит из области памяти 0x00000000, избегая при этом каких-либо ограничений.

Когда вы используете булавки для ремешка boot0 / boot1, вы можете вместо этого вызвать 0x00000000 для отображения в другом месте, где сгорел в загрузчикежизни.Конечно, этот загрузчик может легко прочитать 0x08000000 и легко имитировать сброс путем ветвления, или он может изменить логику и фактически сбросить (если вы об этом попросите, хотя я не знаю, действительно ли этот загрузчик поддерживает запуск программы).Кто знает, работали ли мы там, мы не могли сказать обязательноВполне возможно, что он всегда загружается в загрузчик, а затем меняет отображение в зависимости от ремешков.

аналогично MMU, но гораздо проще декодировать адреса и совмещать их с псевдонимами довольно легко.если boot0 == 0 и address [31:16] = 0x0000, то address [31:16] = 0x0800 и система памяти декодирует его по другому адресу, так же легко, как писать в C, так же легко в HDLесли не проще.

Это нередко встречается и в микроконтроллерах, и в других, но поскольку микроконтроллеры обычно загружаются с флэш-памяти, но в некоторых архитектурах это же пространство загрузки также является векторной таблицей или таблицей исключений, которую может захотеть rtosчтобы иногда манипулировать, вы видите, что оперативную память можно поменять в этом пространстве, так что процессор «видит» некоторого оперативной памяти после изменения управляющего регистра, где при загрузке он «видит» таблицу векторов на флэш-памяти.это или у вас есть код во флэш-ветке куда-то в ram для вектора без сброса, а затем rtos или любое другое приложение, которое заботится об этом, может вносить изменения во время выполнения в то, какой код фактически запускается для этих исключений или прерываний.

ARM налагает правила адресного пространства для того, где код может выполняться и данные могут жить, и где вы, возможно, захотите запустить свое периферийное адресное пространство, и какое адресное пространство зарезервировано под руку для ресурсов внутри ядра.так что вы иногда увидите, что у ram есть псевдоним по более низкому адресу, что означает, что если вы хотите запустить программу в ram, вы хотите использовать более низкий адрес для выполнения, но можете использовать любой адрес для копирования кода туда.

До разработчиков чипов о том, как просто или сложно сделать это.Для ST это довольно просто, тогда в пакете есть один или несколько загрузочных штырьков, которые, по крайней мере, позволяют вам выбирать между вашим приложением и встроенным загрузчиком, так что пока все stm32, которые я видел, флэш-пространство приложения считается живым по адресу 0x08000000 исопоставлен / псевдоним 0x00000000 для одного из этих режимов загрузки.Если открыто два загрузочных контакта, может существовать до четырех возможных условий загрузки, из которых одно - это приложение с 0x00000000, псевдонимом которого является 0x08000000.

Что касается того, как получить биты во флэш-память, это сильно зависит от инструмента.Такие цепочки инструментов, как gnu, безусловно, создадут файл .bin, где первый байт файла - это первый байт от эльфа, который мы хотим иметь в 0x08000000 (если построен таким образом, если вы построили для 0x02000000, он все равно будет первым байтоми этот код, вероятно, не будет работать).Существуют инструменты, и вы, безусловно, можете написать свой собственный, который, зная, что может загрузить BIN-файл в нужном месте 0x08000000, или вы можете иметь слишком большую запись по адресу 0x00000000 в нужном режиме для программы, которая не слишком велика и имеетэто все еще приземляется в правильном месте, чтобы выполнить при перезагрузке.Также есть или вы можете написать инструменты, которые могут анализировать .elf-файлы, intel hex, motorola srecord и другие, и на основе информации в этих двоичных файлах данные будут загружены в желаемое адресное пространство, при условии, что все без ошибок.

Возможно, вы пытаетесь переосмыслить его, в этом нет никакой магии, инструменты должны делать что-то вменяемое, а вменяемое - это брать двоичный файл из компилятора и помещать его в чип, где мы хотим.Мы несем ответственность за скрипт компоновщика и тому подобное, и, конечно же, за загрузку кода / векторной таблицы, но если мы сделаем это правильно, инструменты, если они спроектированы правильно, поместят биты в нужное место в чипе, и если чип спроектирован правильно, какзадокументировано, затем он загрузится и запустится.

Будет ли на самом деле загрузчик установлен в 0x0?Если да, то какова цель определения секторов памяти в файле компоновщика?

В идеале вы хотите, чтобы ваше приложение или загрузчик, как вы их называли, находились по адресу 0x08000000 в адресном пространстве процессоров.В определенных режимах загрузки (boot0 / boot1) этот адрес также имеет псевдоним 0x00000000, поэтому вы можете видеть эту векторную таблицу в обоих местах одновременно.Если вы не находитесь в правильном режиме загрузки, тогда только 0x08000000 покажет ваш код.

Это потому, что 0x0 принадлежит флэш-памяти, и когда MCU запускается, вся флэш-память копируется в RAM по адресу0x8000000?Если да, будет ли загрузчик выполняться из флэш-памяти, а весь остальной код - из ОЗУ?

Логика в чипе предназначена для того, чтобы принимать адрес, который процессор помещает на свою адресную шину, и иметьна флэш-накопитель приложения попадает более одного адреса, флэш-приложение не имеет значения 0x08000000, если, например, его флэш-память объемом 16 Кбайт получает адрес только от 0x0000 до 0xFFFF, при обращении к 0x08001234 оно фактически отправляет 0x1234 на контроллер флэш-памяти и / или контроллер флэш-памятиотрубает верх, если он знает, что должен обработать этот запрос.0x00000000, 0x08000000 - это представление процессоров адресного пространства, реальность заключается в том, что верхние биты декодируются и направляют запрос тому, кому он принадлежит, а конечный обработчик, в конечном счете, просматривает младшие биты, чтобы определить, что адресовано.

Например, когда вы доставляете письмо, оно имеет имя и фамилию, адрес улицы, почтовый индекс города-штата.Как только он попадает в нужное почтовое отделение в нужном штате, почтовый адрес - это все, что имеет значение для почтового человека.Как только он попадает в нужный дом, часто первое имя - это все, что имеет значение, остальное можно игнорировать.Здесь нет разницы.Части адреса (часто) становятся безразличными, так как ответственная логика, которая проверяет, что адрес направляет запрос на правильную сторону.

Принимая во внимание вышеупомянутые вопросы, если я ничего не понял,Какая связь / разница между LMA и смещением файла?

формат файла elf является общим, излишним для работы микроконтроллера, но хорошо поддерживается и прост в использовании, почему нет.Адрес загрузочной памяти - это то место, где мы, программист, хотели, чтобы этот код соответствовал взгляду процессоров на мир.С точки зрения readelf, смещение в файле - это смещение для этой информации в файле elf, и оно находится там, где его помещает инструмент, и не имеет других интересных отношений.Или, по крайней мере, не нужно.Objcopy извлечет эти данные из файла и для двоичного файла -O поместит их в своего рода файл образа памяти с копируемым наименьшим адресом со смещением 0 в этом файле и размером, определяемым общим адресным пространством для всехзагружаемые блоки (если вы не используете больше параметров командной строки).Как вы и предполагали, но если вы подумаете об этом и у вас будет ошибка скрипта компоновщика, если у вас будет хотя бы одна инструкция в 0x08000000 и один байт .data в 0x20000000, но вы не сделаете AT>, тогда ваш файл, несмотря на то, чтотри соответствующих байта будут иметь длину 0x20000001 - 0x08000000 байт.(после двоичного кода -O), поэтому не стоит помещать objcopy в ваш make-файл, пока вы не отладите скрипт компоновщика.Представьте себе, что цель, где флэш-память - 0x00000000, а память - 0xE0000000, довольно большие .bin-файлы, пока вы не разберетесь со сценарием компоновщика.

0 голосов
/ 07 февраля 2019

Нет, загрузчик будет в 08000000, как определено в файле elf.

Изображение будет записано во флэш-памяти по этому адресу и выполнено непосредственно оттуда (не скопировано где-либо еще).

Существует несколько недокументированное поведение: эта единичная область перед пропуском реальных данных при создании двоичного изображения.Как комментарий в исходных состояниях BFDlib (https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=bfd/binary.c;h=37f5f9f7363e7349612cdfc8bc579369bbabbc0c;hb=HEAD#l238)

/* The lowest section LMA sets the virtual address of the start
   of the file.  We use this to set the file position of all the
   sections.  */

Самая низкая секция (.bootloader) LMA - 08000000 в вашем .elf, поэтому двоичный файл будет начинаться с этого адреса.
Вы должны взять этот адресучтите и добавьте его в смещение файла при определении адреса на изображении.

Sections:
Idx Name              Size      VMA       LMA       File off  Algn
  0 .text             000001c4  08010000  08010000  00020000  2**0
    /*                                    ^^^^^^^^              */
    /* this section will be at offset 10000 in image            */

                      CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .bootloader       00004000  08000000  08000000  00010000  2**0
    /*                                    ^^^^^^^^              */
    /* this is the lowest LMA in your case it will be used      */
    /* as a start of an image, and this section will be placed  */
    /* directly at start of the image                           */
                      CONTENTS, ALLOC, LOAD, DATA

Memory layout:     Bin. image layout:
000000000                    \ skipped
...       ________________   /
080000000 .bootloader         0
...       ________________
080004000   <gap>          4000
...       ________________
080010000 .text           10000
...       ________________
0800101C4                 101C4

Этот адрес определен в ldscript, поэтому двоичное изображение должно начинаться с фиксированного местоположения. Однако вы должны знать об этом при работе сldscrips и двоичные изображения.

Чтобы подвести итог процесса сборки и перепрошивки:

  1. При компоновке начальный адрес определяется в ldscript, а первый раздел в elf находится там.
  2. При преобразовании в двоичный файл начальный адрес определяется из LMA, а двоичное изображение начинается с этого адреса.
  3. При мигании изображения тот же адрес, что и у флэшера, используется в качестве параметра, поэтому изображение размещается в нужном месте (определенов ldscript).

Обновление: процесс загрузки STM32F4xxx.

Область адреса, начинающаяся с адресаs 0 специально для этих MCU.Он может быть настроен для сопоставления других регионов, таких как флэш-память, SRAM или системное ПЗУ.Они выбраны по пинам BOOTSELx.Со стороны ЦП это выглядит так, как будто вторая копия флэш-памяти (SRAM или системное ПЗУ) появляется по адресу 0.

При запуске ЦП сначала считывает начальный SP с адреса 0 и начальный ПК с адреса 4. Собственно, читает сфлэш-память выполняется.Если код связан с запуском из фактического местоположения флэш-памяти, то исходный компьютер будет указывать на него.В этом случае выполнение начинается с фактического флэш-адреса.

----- Mapped area (mimics contents as flash) ---
       0:          (02001000)         ;
       4:          (0800ABCD) ----.   ; CPU reads PC here
....                              |   ; (it points to flash)
----- FLASH -----                 |
 8000000:           20001000      |   ; initial stack pointer
 8000004:           0800ABCD --.  |   ; address of _start in flash
....                           |  |   
 800ABCD: <_start:> movw ... <-'<-'   ; Code execution starts here

(Примечание: это не относится к шестнадцатеричным изображениям (таким как Intel hex или s-запись), поскольку такие форматы явно определяют адрес загрузки и используются какесть).

...