repstosw 18 29 мая, 2020 Опубликовано 29 мая, 2020 (изменено) · Жалоба Добрый день. Использую тулчейн arm-none-eabi-gcc для ядра ARM Cortex-A8. Язык C++. Столкнулся с проблемой: не создаётся статический конструктор класса и не происходит инициализация в нём. Вот хедер модуля с классом: #ifndef KEX_H #define KEX_H class IClass { public: IClass(int sa, int sb); private: int a, b; }; class TestClass { public: TestClass(const IClass& ic); private: int a1, b1; }; #endif Вот сам класс: #include "kex.h" #include "printf.h" extern void hardware_init(void); IClass::IClass(int sa, int sb) { a = sa; b = sb; } TestClass::TestClass(const IClass& ic) { hardware_init(); //инитим железо printf("\nHi! I'm Constructor ;)"); //выводим по UART строку } Сама программа //......... IClass ic(2,5); //делаем экземпляр класса - уже на момент создания класса, С++ рантайм должен проинитить железо и вывести строку TestClass tc(ic); int main(void) { while(1); //.......... При запуске верхний код не работает - класс не создаётся, нет рантаймовского инита и ничего не печатается по UART. Если сделать так: //......... int main(void) { IClass ic(2,5); //инит железа и вывод строки TestClass tc(ic); while(1); //.......... То работает. Вопрос, как заставить компилятор и линковщик инициализировать и запускать статические классы? Подозреваю, надо в стартапе правильно вызвать инит, а в скрипте линковщика прописать нужные секции. Программист, писавший код, построил программу таким образом, что такого "добра" просто навалом! На C переписать не выйдет, так как много кода с STL, <vector>, list, class P.S. Код успешно работает на TI C6000+ compiler и на ПК Изменено 29 мая, 2020 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
GenaSPB 11 29 мая, 2020 Опубликовано 29 мая, 2020 (изменено) · Жалоба Гуглить про __libc_init_array, Копировать рекомендованные секции из примеров что идут с arm-none-eabi уже не буду... Spoiler // Используется в случае наличия ключа ld -nostartfiles // Так же смотреть вокруг software_init_hook #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ extern int main(void); extern void __libc_init_array(void); void __NO_RETURN _start(void) { __libc_init_array(); // invoke constructors /* Branch to main function */ main(); /* Infinite loop */ for (;;) ; } // call after __preinit_array_xxx and before __init_array_xxx passing void _init(void) { } void * __dso_handle; void _fini(void) { for (;;) ; } void __gxx_personality_v0(void) { } #ifdef __cplusplus } #endif /* __cplusplus */ Изменено 29 мая, 2020 пользователем GenaSPB Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 232 29 мая, 2020 Опубликовано 29 мая, 2020 · Жалоба 18 минут назад, __inline__ сказал: Вопрос, как заставить компилятор и линковщик инициализировать и запускать статические классы? Очень просто: не использовать в конструкторах обращение к железу. Ну или (если сильно нужно использовать), то инитить это железо до сишного стартапа, в ассемблерном коде после reset-вектора. 18 минут назад, __inline__ сказал: P.S. Код успешно работает на TI C6000+ compiler и на ПК Возможно где-то внутри hardware_init() или printf() используются статически инициализированные члены. А на этапе си-стартап-а (из которого вызываются конструкторы), эти члены ещё не проинициализированы в данном случае. А другой компилятор строит инит статических объектов в другом порядке и поэтому там всё работает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
GenaSPB 11 29 мая, 2020 Опубликовано 29 мая, 2020 · Жалоба Вообще-то порядок вызова статических конструкторов не определён. Если есть завязки на это, то создаем класс, и вот в нем уже будет в порядке описания вызываться. У топикстартера проблема иная - не вызывается вообще конструктор (я написал выше что сделать). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 232 29 мая, 2020 Опубликовано 29 мая, 2020 · Жалоба 24 минуты назад, GenaSPB сказал: У топикстартера проблема иная - не вызывается вообще конструктор Откуда Вы знаете? Из исходного сообщения это никак не следует. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
GenaSPB 11 29 мая, 2020 Опубликовано 29 мая, 2020 (изменено) · Жалоба 4 minutes ago, jcxz said: Откуда Вы знаете? Из исходного сообщения это никак не следует. там только эта проблема и есть... Хотя там и завязка на порядок. Изменено 29 мая, 2020 пользователем GenaSPB Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
RadiatoR 2 29 мая, 2020 Опубликовано 29 мая, 2020 · Жалоба Сталкивался с необходимостью определить порядок инициализации классов. В их конструкторах происходил сетап хардвара. Решил вопрос синглтонами. То есть создавался синглотон класса хардварного модуля и при первом его вызове происходила инициализация этого модуля. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 29 мая, 2020 Опубликовано 29 мая, 2020 (изменено) · Жалоба OK, попробую обмозговать. Результат напишу. Проблема возникла с этим движком: https://github.com/moai/moai-beta/tree/master/3rdparty/box2d-2.2.1 Под ПК и C6745 работает отлично, а под ARM не заработал. В ходе разбирательств с другим программистом, выяснили, что движок зацикливается при обходе двоичного дерева. Это привело к тому что мы обнаружили, что не вызывается конструктор класса. b2World::b2World(const b2Vec2& gravity) И при создании ground Fixture входит в бесконечный цикл. Если же внутренности вставить в main, то движок начинает работать. Но это не совсем хорошее решение, так как предполагаем, что ещё кучу всего остального нужно инитить для верной работы :) Поэтому желается принудительно заствить GCC-компилер сохранять и исполнять конструкторы. Задача может не эмбиддерская, но портирование софта с ПК на МК вполне себе имеет право существовать. :) Нечто подобное было для Keil ARM, под STM32. Там всё зависало и решилось удачно с флагами: --no-exceptions --no-exceptions_unwind --force_new_nothrow А для thumb-режима ещё нужно было принудительно задать выравнивание данных на 4, так как используются пересылки словами: --pointer_alignment=4 --min_array_alignment=4 Изменено 29 мая, 2020 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 29 мая, 2020 Опубликовано 29 мая, 2020 · Жалоба 2 часа назад, GenaSPB сказал: Гуглить про __libc_init_array __lib_init_array достаточно тяжёлая. Если размер актуален, то можно её подменить. На Cortex-Mx я вот так делают. Думаю на Cortex-A буде что-то подобное. Код из стартапа. void SystemInit(); void __libc_init_array(); int main() __attribute__((noreturn)); // These magic symbols are provided by the linker. extern void *_estack; extern void *_sidata, *_sdata, *_edata; extern void *_sbss, *_ebss; extern void (*__preinit_array_start[]) (void) __attribute__((weak)); extern void (*__preinit_array_end[]) (void) __attribute__((weak)); extern void (*__init_array_start[]) (void) __attribute__((weak)); extern void (*__init_array_end[]) (void) __attribute__((weak)); extern void (*__fini_array_start[]) (void) __attribute__((weak)); extern void (*__fini_array_end[]) (void) __attribute__((weak)); // Iterate over all the preinit/init routines (mainly static constructors). inline void __attribute__((always_inline)) __run_init_array (void) { int count; int i; count = __preinit_array_end - __preinit_array_start; for (i = 0; i < count; i++) __preinit_array_start[i] (); count = __init_array_end - __init_array_start; for (i = 0; i < count; i++) __init_array_start[i] (); } void __attribute__((naked, noreturn)) Reset_Handler() { #ifdef __DEBUG_SRAM__ __set_MSP((uint32_t)&_estack); #endif SystemInit(); // User hardware initialization void **pSource, **pDest; for (pSource = &_sidata, pDest = &_sdata; pDest != &_edata; pSource++, pDest++) *pDest = *pSource; for (pDest = &_sbss; pDest != &_ebss; pDest++) *pDest = 0; //__libc_init_array(); // Use with libc start files instead __run_init_array(); __run_init_array(); // Use with the "-nostartfiles" linker option instead __libc_init_array(); (void)main(); } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 29 мая, 2020 Опубликовано 29 мая, 2020 · Жалоба Всем спасибо! Задача решена. Дополнительно настроил проект, чтобы при выкидывании секций, оптимизатор не задевал нужное. Плюс интегрировал в стартап eGON-заголовок и сделал перенос векторов прерываний. Раньше была куча С- и АСМ- файлов, ссылающихся друг на друга. Стартап (часть на ассемблере): Spoiler .global Header .global Jump .global CLI .global STI .global _stack .global _bss_start .global _bss_end .set UND_STACK_SIZE, 0x8 .set ABT_STACK_SIZE, 0x8 .set FIQ_STACK_SIZE, 0x8 .set IRQ_STACK_SIZE, 0x100 .set SVC_STACK_SIZE, 0x8 .set MODE_USR, 0x10 .set MODE_FIQ, 0x11 .set MODE_IRQ, 0x12 .set MODE_SVC, 0x13 .set MODE_ABT, 0x17 .set MODE_UND, 0x1B .set MODE_SYS, 0x1F .equ I_F_BIT, 0xC0 .text .code 32 .section .keep,"a" Header: b Entry @ точка входа в код .long 0x4E4F4765 @ eGON .long 0x3054422E @ .BT0 .long 0x00000000 @ checksum for boot0 (must be calculated !!!) .long 0x00007E00 @ length for boot0 (32256 B) .long 0x00000000 .long 0x00000000 .long 0x00000000 .align 5 @ align 32 byte Vector: LDR pc,=ResetHandler @ Reset LDR pc,=UndefHandler @ Undef LDR pc,=SWIHandler @ SWI LDR pc,=PrefetchHandler @ Prefetch LDR pc,=AbortHandler @ Abort NOP @ Reserved LDR pc,=IRQHandler @ IRQ LDR pc,=FIQHandler @ FIQ NMI ResetHandler: B ResetHandler UndefHandler: B UndefHandler SWIHandler: B SWIHandler PrefetchHandler: B PrefetchHandler AbortHandler: B AbortHandler FIQHandler: B FIQHandler IRQHandler: PUSH {r0-r3,r12,lr} @ BL IRQ_HANDLER POP {r0-r3,r12,lr} SUBS pc,lr,#4 Entry: MRC p15, 0, r0, c1, c0, 0 @ Read CP15 System Control register BIC r0, r0, #(0x1 << 12) @ Clear I bit 12 to disable I Cache BIC r0, r0, #(0x1 << 2) @ Clear C bit 2 to disable D Cache BIC r0, r0, #0x1 @ Clear M bit 0 to disable MMU BIC r0, r0, #(0x1 << 11) @ Clear Z bit 11 to disable branch prediction MCR p15, 0, r0, c1, c0, 0 @ Write value back to CP15 System Control register MOV r0,#0 MCR p15, 0, r0, c8, c7, 0 @ I-TLB and D-TLB invalidation MCR p15, 0, r0, c7, c5, 6 @ BPIALL - Invalidate entire branch predictor array LDR r0, =_stack @ Read the stack address MSR cpsr_c, #MODE_UND|I_F_BIT @ switch to undef mode MOV sp,r0 @ write the stack pointer SUB r0, r0, #UND_STACK_SIZE @ give stack space MSR cpsr_c, #MODE_ABT|I_F_BIT @ Change to abort mode MOV sp, r0 @ write the stack pointer SUB r0,r0, #ABT_STACK_SIZE @ give stack space MSR cpsr_c, #MODE_FIQ|I_F_BIT @ change to FIQ mode MOV sp,r0 @ write the stack pointer SUB r0,r0, #FIQ_STACK_SIZE @ give stack space MSR cpsr_c, #MODE_IRQ|I_F_BIT @ change to IRQ mode MOV sp,r0 @ write the stack pointer SUB r0,r0, #IRQ_STACK_SIZE @ give stack space MSR cpsr_c, #MODE_SVC|I_F_BIT @ change to SVC mode MOV sp,r0 @ write the stack pointer SUB r0,r0, #SVC_STACK_SIZE @ give stack space MSR cpsr_c, #MODE_SYS|I_F_BIT @ change to system mode MOV sp,r0 @ write the stack pointer @ Invalidate and Enable Branch Prediction MOV r0, #0 MCR p15, #0, r0, c7, c5, #6 ISB MRC p15, #0, r0, c1, c0, #0 ORR r0, r0, #0x00000800 MCR p15, #0, r0, c1, c0, #0 @Neon @ MRC p15, #0, r1, c1, c0, #2 @ r1 = Access Control Register @ ORR r1, r1, #(0xf << 20) @ enable full access for p10,11 @ MCR p15, #0, r1, c1, c0, #2 @ Access Control Register = r1 @ MOV r1, #0 @ MCR p15, #0, r1, c7, c5, #4 @flush prefetch buffer @ MOV r0,#0x40000000 @ FMXR FPEXC, r0 @ Set Neon/VFP Enable bit @BSS Clear @ LDR r0, =_bss_start @ Start address of BSS @ LDR r1, =(_bss_end - 0x04) @ End address of BSS @ MOV r2, #0 @Loop: @ STR r2, [r0], #4 @ Clear one word in BSS @ CMP r0, r1 @ BLE Loop @ Clear till BSS end @SetVector mrc p15,0,r0,c1,c0,0 bic r0,r0,#0x2000 mcr p15,0,r0,c1,c0,0 ldr r0,=Vector mcr p15,0,r0,c12,c0,0 @RuntimeInit LDR r10,=RuntimeInit BX r10 Jump: mov pc,r0 CLI: mrs r0, cpsr orr r0, r0, #I_F_BIT msr cpsr_c, r0 mov pc, lr STI: mrs r0, cpsr bic r0, r0, #I_F_BIT msr cpsr_c, r0 mov pc, lr .end Стартап (часть на С): Spoiler extern void HardwareInit(void); //инит железа //extern void __libc_init_array(); //стандартная функция extern int main() __attribute__((noreturn)); //main.cpp //These magic symbols are provided by the linker. extern void *_estack; extern void *_sidata,*_sdata,*_edata; extern void *_sbss,*_ebss; extern void (*__preinit_array_start[]) (void) __attribute__((weak)); extern void (*__preinit_array_end[]) (void) __attribute__((weak)); extern void (*__init_array_start[]) (void) __attribute__((weak)); extern void (*__init_array_end[]) (void) __attribute__((weak)); extern void (*__fini_array_start[]) (void) __attribute__((weak)); extern void (*__fini_array_end[]) (void) __attribute__((weak)); /* Iterate over all the preinit/init routines (mainly static constructors) Use with the "-nostartfiles" linker option instead __libc_init_array() */ inline void __attribute__((always_inline)) __run_init_array(void) { int count; int i; count=__preinit_array_end-__preinit_array_start; for(i=0;i<count;i++)__preinit_array_start[i](); #ifdef HAVE_INIT_FINI _init(); #endif count=__init_array_end-__init_array_start; for(i=0;i<count;i++)__init_array_start[i](); } void __attribute__((naked, noreturn)) RuntimeInit(void) { HardwareInit(); void **pSource,**pDest; for(pSource=&_sidata,pDest=&_sdata;pDest!=&_edata;pSource++,pDest++)*pDest=*pSource; for(pDest=&_sbss;pDest!=&_ebss;pDest++)*pDest=0; __run_init_array(); //__libc_init_array(); (void)main(); } Работают оба способа - через стандартный __libc_init_array и через облегчённый. Скрипт линкера: Spoiler /* linker script */ MEM_SIZE = 0x00007E00 ; /* размер доступной памяти */ ROM_BASE = 0x00000000 ; /* стартовый адрес */ ROM_SIZE = 0x00007000 ; RAM_BASE = ROM_BASE + ROM_SIZE ; RAM_SIZE = MEM_SIZE - ROM_SIZE ; ENTRY(Header) _Minimum_Stack_Size = 0x100 ; MEMORY { ROM (XR) : ORIGIN = ROM_BASE, LENGTH = ROM_SIZE RAM (RW) : ORIGIN = RAM_BASE, LENGTH = RAM_SIZE } /* higher address of the user mode stack */ PROVIDE ( _estack = ALIGN(ORIGIN(RAM) + LENGTH(RAM) - 8 ,8) ); SECTIONS { .text : { . = ALIGN(4); KEEP(*(.keep)); *(.text) /* remaining code */ *(.text.*) *(.rodata) /* read-only data (constants) */ *(.rodata*) *(.eh_frame_hdr) *(.eh_frame) *(.ARM.extab* .gnu.linkonce.armextab.*) *(.gcc_except_table) *(.eh_frame_hdr) *(.eh_frame) *(.glue_7) *(.glue_7t) . = ALIGN(4); } > ROM .preinit_array : { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); } > ROM .init_array : { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); } > ROM .fini_array : { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); } > ROM /* .ARM.exidx is sorted, so has to go in its own output section. */ __exidx_start = .; .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } > ROM __exidx_end = .; .text.align : { . = ALIGN(8); _etext = .; _sidata = _etext; /* start of initialized data label */ } > ROM .data : AT ( _sidata ) /* AT makes the LMA follow on in the binary image */ { . = ALIGN(4); _sdata = .; /* start of .data label */ KEEP( *(.data) ) KEEP( *(.data.*) ) . = ALIGN(4); _edata = .; /* end of .data label */ } > RAM /* .bss section - uninitialized data */ .bss : { . = ALIGN(4); _sbss = .; /* start of .bss label (for startup) */ *(.bss) *(.bss.*) *(COMMON) . = ALIGN(4); _ebss = .; /* end of .bss label (for startup) */ _end = .; /* end of used ram (start of free memory, for malloc) */ __end = .; /* the same */ } > RAM __bss_start__ = _sbss ; __bss_end__ = _ebss ; /* * This is the user stack section * This is just to check that there is enough RAM left for the User mode stack * It should generate an error if it's full. */ ._usrstack : { . = ALIGN(4); _susrstack = . ; . = . + _Minimum_Stack_Size ; . = ALIGN(4); _eusrstack = . ; } > RAM _stack = _eusrstack ; PROVIDE( _heap = _ebss ); PROVIDE ( _eheap = ALIGN(ORIGIN(RAM) + LENGTH(RAM) - 8 ,8) ); /* * after that it's only debugging information. */ /* remove the debugging information from the standard libraries */ DISCARD : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } } Команды GCC: del *.o del *.elf del *.asm del *.bin del *.fil del *.a13 echo MAIN FILE arm-none-eabi-g++.exe -O3 -Ofast -marm -march=armv7-a -fmax-errors=1 -ffunction-sections -fdata-sections -fno-exceptions -include aw_sdk/sdk.h -c led.cpp echo CPP CLASS arm-none-eabi-g++.exe -O3 -Ofast -marm -march=armv7-a -fmax-errors=1 -ffunction-sections -fdata-sections -fno-exceptions -Iaw_sdk/uart -c kex.cpp ... echo C STARTUP arm-none-eabi-gcc.exe -O3 -Ofast -marm -march=armv7-a -fmax-errors=1 -ffunction-sections -fdata-sections -fno-exceptions -Iaw_sdk -Iaw_sdk/core_v7 -Iaw_sdk/uart -c startup_c.c echo HARDWARE INIT arm-none-eabi-gcc.exe -O3 -Ofast -marm -march=armv7-a -fmax-errors=1 -ffunction-sections -fdata-sections -fno-exceptions -Iaw_sdk -Iaw_sdk/core_v7 -Iaw_sdk/uart -c hardware.c echo ASM STARTUP arm-none-eabi-as.exe -o startup_asm.o startup_asm.S echo LINKER arm-none-eabi-g++.exe -T led.lds -Wl,--gc-sections -Wl,--static -nostartfiles led.o kex.o startup_c.o startup_asm.o hardware.o -o led.elf -Xlinker -Map=led.map echo DISASSEMBLER arm-none-eabi-objdump.exe -D led.elf > led.asm echo ELF to BIN arm-none-eabi-objcopy.exe -O binary led.elf led.bin echo ZERO-FILLER up to 0x7E00 bytes filler.exe led.bin led.fil echo CHECKSUMM for eGON checksum.exe led.fil led.a13 echo LOAD ALLWINNER A13 PROGRAM via USB sunxi-fel.exe -v spl led.a13 Сам главный файл и класс: Spoiler //kex.h #ifndef KEX_H #define KEX_H class IClass { public: IClass(int sa, int sb); private: int a, b; }; class TestClass { public: TestClass(const IClass& ic); private: int a1, b1; }; #endif //kex.cpp #include "kex.h" #include "printf.h" IClass::IClass(int sa,int sb) { a=sa; b=sb; } TestClass::TestClass(const IClass& ic) { printfx("\nHi! I'm Constructor ;)"); } //main #include "kex.h" IClass ic(2,5); TestClass tc(ic); int main(void) { printf("\nA13 Class Test...\n"); Loop: goto Loop; return 0; } Результат: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
GenaSPB 11 29 мая, 2020 Опубликовано 29 мая, 2020 · Жалоба Для истории еще скрипт линкера... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 29 мая, 2020 Опубликовано 29 мая, 2020 · Жалоба 2 hours ago, GenaSPB said: Для истории еще скрипт линкера... там же выше! Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 31 мая, 2020 Опубликовано 31 мая, 2020 (изменено) · Жалоба Вот ещё информация по теме. + конструкторы-дестуркторы в "обычном" С: Кстати, так как в моём случае вся память R/W, то надобность в копировании LMA в VMA отпадает и скрипт линкера можно упростить: переменные с начальным ненулевым инитом пойдут в RO-data в секцию text. И туда можно писать, так как это тоже RAM, а не Flash. Это позволило сэкономить немного памяти и упростить жизнь си-шному рантайму :) Вот это можно исключить, ненулевые переменные пойдут в .text, а нулевые в .bss .text.align : { . = ALIGN(8); _etext = .; _sidata = _etext; /* start of initialized data label */ } > ROM .data : AT ( _sidata ) /* AT makes the LMA follow on in the binary image */ { . = ALIGN(4); _sdata = .; /* start of .data label */ KEEP( *(.data) ) KEEP( *(.data.*) ) . = ALIGN(4); _edata = .; /* end of .data label */ } > RAM Изменено 31 мая, 2020 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться