Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: gnu ld - как сделать "дырку" в памяти
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > GNU/OpenSource средства разработки
Непомнящий Евгений
Мне надо расположить прошивку в нижней и верхней части флеша, с пустым местом посередине.
Оптимально - чтобы было занято нижние 4к и столько, сколько нужно - сверху.
Но в принципе пойдет и указать размер верхнего сегмента руками.

Пробовал так:
Код
MEMORY
{  
  ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x0000C000
  rom1 (rx)  : ORIGIN = 0x08000000, LENGTH = 0x00001000
  rom2 (rx)  : ORIGIN = 0x08000000 + 0x00040000 - 0x0001000, LENGTH = 0x00001000
}

SECTIONS
{
    .text :
    {  
        KEEP(*(.vectors))
        *(.text .text.*)                           
        *(.rodata)                                               
    } > rom1    

    .text2 :
    {        
        *(.text .text.*)                           
        *(.rodata)                                               
    } > rom2
  
   ...
}


Ругается section `.text' will not fit in region `rom1'.

Понятно, что можно руками распихать разные файлы по разным секциям, но нет ли способа сделать это автоматически?
Непомнящий Евгений
Судя по вот этому http://sourceware.org/ml/binutils/2002-11/msg00066.html, так сделать не получится. А жаль. В иаровском линкере никаких проблем с этим не было...
demiurg_spb
В Makefile
Код
MY_FLASH_ABS_ADDRESS = 0x0F80
LDFLAGS += -Wl,--section-start=.section_my=$(MY_FLASH_ABS_ADDRESS)

В программе
Код
#define PROGMEM_SECTION_MY __attribute__((section(".section_my")))  

const uint8_t  PROGMEM_SECTION_MY   my_data[128] =  {....};

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

PS: проверил - не работаетsad.gif
Ругается что на .text налезает...
Непомнящий Евгений
Да, линкер какой-то недоделанный sad.gif

Вроде и мощный и выражения там есть, а каких-то тривиальных вещей не сделаешь...
MBR
Цитата(Непомнящий Евгений @ Aug 15 2012, 10:40) *
Да, линкер какой-то недоделанный sad.gif

С линкером все в порядке, просто постановка задачи достаточно странная.
Сергей Борщ
QUOTE (MBR @ Aug 16 2012, 07:35) *
С линкером все в порядке, просто постановка задачи достаточно странная.
Постановка нормальная. Задачи бывают разные. У больших MSP430, например, область векторов прибита гвоздями посреди флеша. Или я хочу, чтобы сегменты стека при размещении "прижимались" к концу, а не началу региона. Желание вполне естественное, а линкер такого не позволяет. А ИАР позволял.
aas
Цитата(demiurg_spb @ Aug 15 2012, 09:36) *
В Makefile
Код
MY_FLASH_ABS_ADDRESS = 0x0F80
LDFLAGS += -Wl,--section-start=.section_my=$(MY_FLASH_ABS_ADDRESS)

В программе
Код
#define PROGMEM_SECTION_MY __attribute__((section(".section_my")))  

const uint8_t  PROGMEM_SECTION_MY   my_data[128] =  {....};

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

PS: проверил - не работаетsad.gif
Ругается что на .text налезает...


У нас примерно так и сделано, и все работает. Единственно, __attribute__((section(".section_my"))) стоит в конце, после определения переменной.
используем GNU ARM, и компилятор, и линковщик
demiurg_spb
Цитата(aas @ Aug 16 2012, 10:30) *
У нас примерно так и сделано, и все работает. Единственно, __attribute__((section(".section_my"))) стоит в конце, после определения переменной.
используем GNU ARM, и компилятор, и линковщик

Так и у меня всё работает, пока объём секции .text не превышает объёма N, где N-это стартовый адрес секции .section_my.
Поэтому я свою секцию .section_my прижимаю к концу флэша. Стоит её передвинуть поближе к началу чтобы она была окружена секцией .text как кердык и случается.
Непомнящий Евгений
Цитата(MBR @ Aug 16 2012, 08:35) *
С линкером все в порядке, просто постановка задачи достаточно странная.


Расширенно задача звучит так:
Есть STM32F10xx, в которых загрузчик должен начинаться с начала флеша (так как первые 4к автоматом защищаются от записи при установке защиты на чтение).

Но загрузчик в 4к не влезает, к тому ж он может впоследствии улучшаться (и увеличиваться в размере).
Поэтому фиксировать под него скажем первые 8к флеша неохота.
Идея такая - загрузчик занимает первые 4 к, затем располагается прошивка (всегда по фиксированному адресу 08001000), а затем в верхней области флеша продолжается загрузчик.

Если впоследствии загрузчик вырастет, можно будет взять новый загрузчик и слить со старой прошивкой, не перекомпиливая ее. Ну а если прошивка не помещается в "дырку" - сливатель выдаст ошибку.

Была кстати идея слинковать снача без дырки, затем вычитать из elf размеры всех функций, сгенерить нужный скрипт для линкера (указав порядок размещения функций) и слинковать заново sm.gif

aas
Непомнящий Евгений,
я так понял, с дыркой собирается именно загрузчик, грузится во флеш, а потом основная прошивка как-то отдельно догружается? Или после смены загрузчика перекомпилируются только его файлы, а линкуется все вместе с основной прошивкой (которая не перекомпилируется при этом)?

Ну то есть в любом случае размер основной программы известен заранее, ну и значит положение дырки и размер секции text тоже известен. А для высоких адресов загрузчика создаем отдельную секцию и располагаем ее по заданному адресу. И двигаем этот адрес вниз по мере роста загрузчика

Или я что-то не понял?
Непомнящий Евгений
Цитата(aas @ Aug 16 2012, 21:49) *
Ну то есть в любом случае размер основной программы известен заранее, ну и значит положение дырки и размер секции text тоже известен. А для высоких адресов загрузчика создаем отдельную секцию и располагаем ее по заданному адресу. И двигаем этот адрес вниз по мере роста загрузчика


Все так, но как это сделать? Пока единственный рабочий вариант - руками указывать, какие входные секции куда размещаются, что тоскливо.

Код
.text :
    {  
        KEEP(*(.vectors))
        *file1.obj(.text .text.*)                          
        *file2.obj(.text .text.*)                          
        *file3.obj(.text .text.*)                                  
    } > rom1    

    .text2 :
    {        
        *(.text .text.*)                          
        *(.rodata)                                              
    } > rom2
MBR
Цитата(Непомнящий Евгений @ Aug 16 2012, 14:20) *
Идея такая - загрузчик занимает первые 4 к, затем располагается прошивка (всегда по фиксированному адресу 08001000), а затем в верхней области флеша продолжается загрузчик.

Мое предложение - сделать это первичным и вторичным загрузчиком. Первичный никогда не стирается и всегда занимает меньше 4 к. Основная его задача - инициализация железа и запуск вторичного загрузчика (возможно, обновление себя и вторичного загрузчика). Точка входа во вторичный загрузчик располагается по фиксированному адресу.
Непомнящий Евгений
sm.gif
радикальная идея.

Минусы:
1. надо поместиться в т.ч. в младшие модели с 16к флеши. т.е. общий размер загрузчика не более 8 к. В 8 к у меня с трудом уместился загрузчик без всякого разделения на части
2. усложняются процессы сборки образа и перепрошивки
3. функциональность не делится на две независимые части. А если принудительно поделить - сильно вырастет размер.
MBR
Цитата(Непомнящий Евгений @ Sep 4 2012, 11:40) *
sm.gif
радикальная идея.

Минусы:
1. надо поместиться в т.ч. в младшие модели с 16к флеши. т.е. общий размер загрузчика не более 8 к. В 8 к у меня с трудом уместился загрузчик без всякого разделения на части
2. усложняются процессы сборки образа и перепрошивки
3. функциональность не делится на две независимые части. А если принудительно поделить - сильно вырастет размер.

1. Зачем? Фиксированный адрес джампа, а где будет находиться основной загрузчик - пофигу.
2. Да ладно. Один раз скрипт накататься
3. В том то и суть, что делится. Плюсом, можно вынести работу с железом в первичный загрузчик, а вторичный сделать аппаратно-независимым, только дергать логические функции.

Я подобные схемы очень часто видел в прошивках тех же сотовых телефонов - абстракция протокола загрузчика + защита от взлома через подмену вторичного загрузчика.

8к. У Вас там цифровая подпись что-ли?
Непомнящий Евгений
Тогда плиз напишите поподробнее.
Вот у меня загрузчик.
Его задачи:
1. Ловить байты (работа с железом)
2. Складывать из них пакеты
3. Парсить пакеты, формировать ответы
4. Передавать байты (снова работа с железом)
5. Формировать по мере принятия пакетов образ программы, дешифровать его и программировать флеш
6. По команде извне вычитать программу из устройства - считать из флеша, зашифровать, оформить в пакеты

Как его разделить на две независимые части?
Задачи 1-5 должны присутствовать уже в первой части загрузчика - иначе он не сможет запрограммировать второй загрузчик.
Задача 6 сама по себе весит немного...
MBR
Ну да, вполне стандартные задачи. Обычно у первичного загрузчика свой протокол, у вторичного - поверх первичного - свой. Есть таблица вызова функций для работы с железом. Первичный загрузчик гарантирует доставку пакетов, вторичный - занимается логикой.

Кстати, вторичный загрузчик, вообще можно не держать во флеше. Грузить непосредственно первичным в ram и стартовать оттуда. Решает кучу проблем.
Непомнящий Евгений
Цитата(MBR @ Sep 4 2012, 13:48) *
Обычно у первичного загрузчика свой протокол, у вторичного - поверх первичного - свой.

А зачем второму свой протокол?

Цитата
Есть таблица вызова функций для работы с железом.

В смысле - вторичный загрузчик дергает по этой таблице функции из первичного?

Цитата
Первичный загрузчик гарантирует доставку пакетов, вторичный - занимается логикой.

Ну так получается, что первичный загрузчик должен уже уметь практически все, чтобы запрошить вторичный. Во вторичный особо нечего выносить... Если бы к примеру надо было еще какую-нить дополнительную память писать - датафлеш скажем - ну тогда да, можно было бы вынести в отдельный загрузчик. А так...

Цитата
Кстати, вторичный загрузчик, вообще можно не держать во флеше. Грузить непосредственно первичным в ram и стартовать оттуда. Решает кучу проблем.

А каких? Вроде с флешем только одна проблема - во время записи флеша проц "виснет". При стирании страницы получается ощутимо.

MBR
Цитата(Непомнящий Евгений @ Sep 4 2012, 16:39) *
А зачем второму свой протокол?

Как зачем? Первичный - пакет принял, пакет отправил. Проконтролировал отправку контрольными суммами, если нужно, переслал. Что находится в пакете - ему знать не нужно, он лишь гарантированно доставляет данные, устраняя вероятные ошибки в физическом канале связи. Вторичный - парсит пакеты без траспортных заголовков, определяет, какая там логическая команда, какие данные и занимается непосредственно логикой. OSI же.

Цитата(Непомнящий Евгений @ Sep 4 2012, 16:39) *
В смысле - вторичный загрузчик дергает по этой таблице функции из первичного?

Да. Набор этих функций минимален, и, скорее всего, не будет нуждаться в доработке.

Цитата(Непомнящий Евгений @ Sep 4 2012, 16:39) *
Ну так получается, что первичный загрузчик должен уже уметь практически все, чтобы запрошить вторичный. Во вторичный особо нечего выносить... Если бы к примеру надо было еще какую-нить дополнительную память писать - датафлеш скажем - ну тогда да, можно было бы вынести в отдельный загрузчик. А так...

Надо исходить из того, что основные задачи первичного загрузчика:
- транспортный канал с хостом
- загрузка вторичного загрузчика через канал связи
- обеспечить проверку подлинности и запуск вторичного загрузчика.
- запустить основную прошивку, если условия запуска загрузчика не выполнены
Все это занимает меньше килобайта.

Цитата(Непомнящий Евгений @ Sep 4 2012, 16:39) *
А каких? Вроде с флешем только одна проблема - во время записи флеша проц "виснет". При стирании страницы получается ощутимо.

Главное: не нужно поддерживать на уровне хоста зоопарк версий. Прогрузили вторичный загрузчик, запустили - вот и новый протокол. Можно их делать несколько - для каждой задачи - прошивка основного флеша, прошивка датафлеш. Да хоть сброс настроек.
Непомнящий Евгений
Цитата(MBR @ Sep 5 2012, 09:36) *
Как зачем? Первичный - пакет принял, пакет отправил. Проконтролировал отправку контрольными суммами, если нужно, переслал. Что находится в пакете - ему знать не нужно, он лишь гарантированно доставляет данные, устраняя вероятные ошибки в физическом канале связи. Вторичный - парсит пакеты без траспортных заголовков, определяет, какая там логическая команда, какие данные и занимается непосредственно логикой. OSI же.

Если первому загрузчику надо заливать второй, то так не получится - ему придется парсить пакеты и отвечать на них... А заливка второго загрузчика ничем не отличается от заливки самой программы (по крайней мере в моем случае, когда прога живет во встроенном флеше). Так что опять получается, что в первом загрузчике сидит все, а во втором - почти ничего

Цитата
Надо исходить из того, что основные задачи первичного загрузчика:
- транспортный канал с хостом
- загрузка вторичного загрузчика через канал связи
- обеспечить проверку подлинности и запуск вторичного загрузчика.
- запустить основную прошивку, если условия запуска загрузчика не выполнены
Все это занимает меньше килобайта.

Ну хз насчет килобайта. У меня практически эта функциональность + шифрование + еще кое-что по мелочи едва влезла в 8 к. Компилятор gnu gcc от бывшего кодесоурсери. Писал на С с классами, без всяких наворотов, CMSIS юзал по минимуму.


Цитата
Главное: не нужно поддерживать на уровне хоста зоопарк версий. Прогрузили вторичный загрузчик, запустили - вот и новый протокол. Можно их делать несколько - для каждой задачи - прошивка основного флеша, прошивка датафлеш. Да хоть сброс настроек.

А зачем нам новый протокол? Опять таки, функциональность устройства по железу фиксирована на момент заливки туда основного загрузчика. Т.е. если в основном загрузчике иметь разные варианты компиляции в зависимости от устройства - это с головой хватит.
_Артём_
Цитата(Непомнящий Евгений @ Sep 5 2012, 08:49) *
Ну хз насчет килобайта. У меня практически эта функциональность + шифрование + еще кое-что по мелочи едва влезла в 8 к. Компилятор gnu gcc от бывшего кодесоурсери. Писал на С с классами, без всяких наворотов, CMSIS юзал по минимуму.

Что-то многовато. Ищи там ФУНКЦИОНАЛ навороченный?
На АВР приём-передача по UART-запись-чтение прошивки в dataflash-простейшее шифрование в 4КБ влезало. В 8 наверное AES влезет и приём-передача через GSM например (GSM не проверял).
MBR
Цитата(Непомнящий Евгений @ Sep 5 2012, 09:49) *
Если первому загрузчику надо заливать второй, то так не получится - ему придется парсить пакеты и отвечать на них... А заливка второго загрузчика ничем не отличается от заливки самой программы (по крайней мере в моем случае, когда прога живет во встроенном флеше). Так что опять получается, что в первом загрузчике сидит все, а во втором - почти ничего

Там пакета-то - код команды, блок данных, crc. Ответ ack/nack. Это 5 строчек на сях. Получить блок, проверить crc, скопировать в область памяти, увеличить счетчик. По команде запуска джампнуть на адрес. И 3 команды в минимуме - handshake + load secondary, read/write.

Цитата(Непомнящий Евгений @ Sep 5 2012, 09:49) *
Ну хз насчет килобайта. У меня практически эта функциональность + шифрование + еще кое-что по мелочи едва влезла в 8 к. Компилятор gnu gcc от бывшего кодесоурсери. Писал на С с классами, без всяких наворотов, CMSIS юзал по минимуму.

Имхо, си c классами в загрузчике, это слишком жирно.

Цитата(Непомнящий Евгений @ Sep 5 2012, 09:49) *
А зачем нам новый протокол?

Баги, новые команды. Да что угодно. Если устройств выпустится достаточное количество, поддерживать их будет уже геморно.
Непомнящий Евгений
Цитата(_Артём_ @ Sep 5 2012, 10:04) *
Что-то многовато. Ищи там ФУНКЦИОНАЛ навороченный?
На АВР приём-передача по UART-запись-чтение прошивки в dataflash-простейшее шифрование в 4КБ влезало. В 8 наверное AES влезет и приём-передача через GSM например (GSM не проверял).

IAR AVR как-то получше оптимизировал. На нем в 8К помимо того, что влезло в АРМ, втиснулась еще и индикация на дисплее. Шифрование у нас ГОСТ, прием-передача конкретно в этом случае по самодельному интерфейсу, чем-то похожему на SPI.

Функционал:
1. Запись прошивки (+ проверка на совместимость + дешифровка + проверка цифровой подписи)
2. Чтение прошивки (+ шифровка, цифровая подпись)
3. Чтение информации об устройстве и статуса
4. Широковещательная запись (похожа на 1, но немного другой алгоритм)
5. Всякие мелочи - запуск программы, принудительный переход в программирование

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

Писал на С с классами (просто данные и методы собирал в класс). АСМ не юзал.


Цитата(MBR @ Sep 5 2012, 10:07) *
Там пакета-то - код команды, блок данных, crc. Ответ ack/nack. Это 5 строчек на сях. Получить блок, проверить crc, скопировать в область памяти, увеличить счетчик. По команде запуска джампнуть на адрес. И 3 команды в минимуме - handshake + load secondary, read/write.

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

Цитата
Имхо, си c классами в загрузчике, это слишком жирно.

Ну может быть. С другой стороны, чем это сильно отличается от функций + глобальных переменных? Единственное отличие - каждая функция получает доп указатель. Но у СТМ по идее тут должно быть все хорошо. Хотя надо ради интереса один класс сделать на статических переменных\функциях, возможно действительно что-то ужмется.


Цитата
Баги, новые команды. Да что угодно. Если устройств выпустится достаточное количество, поддерживать их будет уже геморно.

Баги и новые команды сидят в самой прошивке. Если они вдруг поселятся в первичном загручике - все равно его перепрошить не получится...
_Артём_
Цитата(Непомнящий Евгений @ Sep 5 2012, 09:29) *
IAR AVR как-то получше оптимизировал. На нем в 8К помимо того, что влезло в АРМ, втиснулась еще и индикация на дисплее. Шифрование у нас ГОСТ, прием-передача конкретно в этом случае по самодельному интерфейсу, чем-то похожему на SPI.

А оптимизация какая использовалась?
Заметил, что в отличии от ИАРа GCC реально оптимизирует по размеру - чуть ли не в полтора раза ужимает.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.