Darth Vader 0 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба Как в Си-файле выделить место под стек и кучу? Компилятор ARM 5.06 (среда Keil MDK ARM 5.24). Имеется стандартный ассемблерный файл Startup.s из CMSIS. Spoiler ;<h> Stack Configuration ; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8> ;</h> Stack_Size EQU 0x00000400 AREA STACK, NOINIT, READWRITE, ALIGN=3 __stack_limit Stack_Mem SPACE Stack_Size __initial_sp ;<h> Heap Configuration ; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8> ;</h> Heap_Size EQU 0x00000C00 IF Heap_Size != 0 ; Heap is provided AREA HEAP, NOINIT, READWRITE, ALIGN=3 __heap_base Heap_Mem SPACE Heap_Size __heap_limit ENDIF Показан фрагмент, где резервируется память под стек и кучу. Хочу сделать его аналог на Си. Как объявлять таблицу векторов прерываний много где описано - в любой теме про загрузчики есть эта информация. А вот как выделить (зарезервировать) память под стек и кучу нигде не находил. Предлагаю здесь обсудить этот вопрос. Изначально я хотел сделать так: объявить два массива байт с выравниванием по границе 8 байт: один под стек, второй под кучу. Размеры задать константами, объявленными, как макросы через #define. Адрес конца массива под стек занести в начало таблицы векторов прерываний. Встал вопрос: а нужно ли как-то сообщить компилятору/линкеру о размещении и размерах стека и кучи? Оказалось, что да, надо. Здесь пишут, что надо определить имена: __initial_sp, __heap_base, __heap_limit. Вот по их использованию есть несколько вопросов: 1. В примере по ссылке в ассемблерной вставке эти имена только определяются. Резервирование памяти в явном виде, как в ассемблерном стартапе, не происходит. Вопрос: почему? Ведь в ассемблерном стартапе память под стек и кучу явно выделяется директивой SPACE. 2. Почему их надо определять именно в ассемблерном коде? Почему нельзя определить в Си-коде через #define? Зачем для этого городить ассемблерные вставки? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 40 minutes ago, Darth Vader said: память под стек и кучу нигде не находил. Этим линкер занимается, читайте информацию про него. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 61 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 1 hour ago, Darth Vader said: Хочу сделать его аналог на Си. А в чем смысл, можете пояснить? В "C" можно было только __user_initial_stackheap() использовать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Eddy_Em 1 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба Как выше было сказано, место резервируется не в стартапе, а в линкере. В стартапе лишь берут данные из линкера и затем инициализируют все необходимое. Я утащил сишный стартап из opencm3. Там прекрасно видно вот это: extern unsigned _data_loadaddr, _data, _edata, _ebss, _stack; extern funcp_t __preinit_array_start, __preinit_array_end; extern funcp_t __init_array_start, __init_array_end; extern funcp_t __fini_array_start, __fini_array_end; сами адреса вычисляются линкером из ld-скрипта. А уж в линкере можно и свои секции объявлять - хоть во флеше, хоть в памяти... Я себе во флеш две секции добавил, чтобы иметь область для сохранения конфигурационных данных и логов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 13 minutes ago, aaarrr said: В "C" можно было только __user_initial_stackheap() использовать. Так делаю, причем аж в файле *.cpp. Читать тут: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0099a/armlib_cihhdahf.htm Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexandrY 2 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 2 hours ago, Darth Vader said: Оказалось, что да, надо. Здесь пишут, что надо определить имена: __initial_sp, __heap_base, __heap_limit. Заметьте, там все время повторяется будете или нет вы использовать стандартные библиотеки Keil и какие. Некоторые опенсорсные проекты и большинство RTOS игнорят эти библиотеки, так что в приложении от штатной установки стека и кучи будет ни холодно, ни жарко. Поэтому разумнее их назначить не через линкер, а через объявление массивов и переопределение специальных функций, как советуется по вашей ссылке. Потом эти массивы можно переиспользовать в настоящей куче назначаемой в приложении. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 61 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 17 minutes ago, AlexandrY said: Поэтому разумнее их назначить не через линкер, а через объявление массивов и переопределение специальных функций, как советуется по вашей ссылке. Потом эти массивы можно переиспользовать в настоящей куче назначаемой в приложении. Назначение секций линкером никак не помешает использовать их повторно в "настоящей" куче. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexandrY 2 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 8 minutes ago, aaarrr said: Назначение секций линкером никак не помешает использовать их повторно в "настоящей" куче. Речь не про то что в принципе помешает или нет, а про более эффективные практики. По ссылке не менее 3-х методов, но какой наиболее полезный? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 61 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 2 hours ago, AlexandrY said: По ссылке не менее 3-х методов, но какой наиболее полезный? Ну так расскажите, а мы послушаем. Как еще обучиться эффективным практикам-то Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 29 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба Определить и разместить стек и кучу в С-ном файле несложно. Но у стандартных библиотек есть вызов функции __user_initial_stackheap() в которой стартап должен вернуть в регистрах R0,R1,R2,R3 размер и расположение стека и кучи. Как это сделать без асм я что-то не допёр. Прикладываю мои упражнения по созданию кейловского стартапа на Си. STM32F411VE-SRAM.sct startup_stm32f411xe.c Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 29 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 5 часов назад, Eddy_Em сказал: Там прекрасно видно вот это Да кто ж тебя про GCC спрашивал то? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 1 hour ago, VladislavS said: Определить и разместить стек и кучу в С-ном файле несложно. Но у стандартных библиотек есть вызов функции __user_initial_stackheap() в которой стартап должен вернуть в регистрах R0,R1,R2,R3 размер и расположение стека и кучи. Как это сделать без асм я что-то не допёр. using StackItem = uint64_t; using StackSize = uint32_t; using HeapItem = uint64_t; struct InitialStackHeapConfig { unsigned heapBase; // low-address end of initial heap unsigned stackBase; // high-address end of initial stack unsigned heapLimit; // high-address end of initial heap unsigned stackLimit; // unused }; #define DEFAULT_HEAP_SIZE_BYTES (4096) #define STARTUP_STACK_SIZE_BYTES (512) __attribute__((used)) __attribute__((section(".stack"))) __attribute__((aligned(sizeof(StackItem)))) static StackItem startupStack[STARTUP_STACK_SIZE_BYTES / sizeof(StackItem)] ; __attribute__((used)) __attribute__((section(".heap"))) __attribute__((aligned(sizeof(HeapItem)))) static HeapItem defaultHeap[DEFAULT_HEAP_SIZE_BYTES / sizeof(HeapItem)]; extern "C" __attribute__((value_in_regs)) struct InitialStackHeapConfig __user_setup_stackheap(unsigned R0, unsigned SP, unsigned R2, unsigned SL) { struct InitialStackHeapConfig config; config.heapBase = reinterpret_cast<unsigned int>(defaultHeap); config.stackBase = reinterpret_cast<unsigned int>(startupStack) + STARTUP_STACK_SIZE_BYTES; config.heapLimit = reinterpret_cast<unsigned int>(defaultHeap) + DEFAULT_HEAP_SIZE_BYTES; config.stackLimit = reinterpret_cast<unsigned int>(startupStack); // unused return config; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба Разумеется, компилятор v6. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 29 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба Спасибо, будем знать. Вся хитрость в нестандартном __attribute__((value_in_regs)) А не подскажешь как надёжно узнать, что тебя из-под Keil компилят, а то эта редиска на __GNUC__ отзывается. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 17 21 ноября, 2019 Опубликовано 21 ноября, 2019 · Жалоба 2 minutes ago, VladislavS said: А не подскажешь как надёжно узнать, что тебя из-под Keil компилят, а то эта редиска на __GNUC__ отзывается. Не знаю, но я бы начал с гугля, потом мануала на компилятор, на край порылся бы в исходниках стандартных библиотек под этот компилятор. 4 minutes ago, VladislavS said: Вся хитрость в нестандартном Это все есть в мануале, оттуда и заимствовано. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться