Перейти к содержанию
    

Проблема с константами в классе.

Есть некий базовый класс:

 

class uart {

protected:

const uint8_t num;

const uint8_t channel;

volatile uint32_t time;

uint8_t rxCount;

uint8_t echoMode;

__uartState state;

uint32_t tx_pointer;

uint32_t rx_pointer;

uint32_t size;

uint32_t txSize;

uint8_t rxEchoSize;

uint8_t error[2];

uint16_t crc;

public:

uart(const uint8_t n, const uint8_t c) : num(n), channel( c) { // Конструктор

time = tx_pointer = rx_pointer = size = 0;

error[0] = error[1] = 0;

};

...

 

есть от него наследник:

 

class mco_uart : public uart {

public:

uint8_t buf[2][MCO_BUFF_SIZE];

mco_uart(const uint8_t n, const uint8_t c) : uart (n, c) {}; // Конструктор

...

 

создаю экземпляры класса:

 

mco_uart extUart[EXTUART_NUM] = {

mco_uart(0, 4 << 4), mco_uart(0, 5 << 4), mco_uart(0, 6 << 4), mco_uart(0, 7 << 4),

mco_uart(1, 4 << 4), mco_uart(1, 5 << 4), mco_uart(1, 6 << 4), mco_uart(0, 1 << 4),

mco_uart(0, 2 << 4), mco_uart(0, 3 << 4), mco_uart(1, 0 << 4), mco_uart(1, 1 << 4),

mco_uart(1, 2 << 4), mco_uart(1, 3 << 4)

};

 

В результате переменные num и channel равны 0.

В иаре всё работает то есть значения переменным присваивается правильно, а в gcc присваивания нет.

Что я делаю не так? На плюсах пишу первый раз, до этого писал только на чистом C. Хочется перейти на gcc и не получается!

Компилятор от клёна kgp_arm_eabi_20100509

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В результате переменные num и channel равны 0.

В иаре всё работает то есть значения переменным присваивается правильно, а в gcc присваивания нет.

Что я делаю не так? На плюсах пишу первый раз, до этого писал только на чистом C. Хочется перейти на gcc и не получается!

Компилятор от клёна kgp_arm_eabi_20100509

 

mco_uart extUart[EXTUART_NUM] глобальный объект или на стеке ?

 

конструкторы статических объектов вызываются?

 

давайте кусок кода который это воспроизведет, без лишних деталей.

 

я не телепат но скорее всего просто конструкторы статических глобальных объектов не вызываются.

 

чтобы это работало в скрипт линкера долны быть добавлены

/* .ctors .dtors are used for c++ constructors/destructors */
.ctors :
{
    PROVIDE(__ctors_start__ = .);
    KEEP(*(SORT(.ctors.*)))
    KEEP(*(.ctors))
    PROVIDE(__ctors_end__ = .);
} >ROM
.dtors :
{
    PROVIDE(__dtors_start__ = .);
    KEEP(*(SORT(.dtors.*)))
    KEEP(*(.dtors))
    PROVIDE(__dtors_end__ = .);
} >ROM

в сило того что сами по себе в коде конструкторы статических глобальных объекто не вызываются линкер их нахрен выкидывает дкумая что это лишний код

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

далее ктото должен вызвать эти конструкторы до вызова main. это обычно принято делать в crt коде.

вставте полсле инициализации данных в crt следующий код;

 DR R0, =__ctors_start__
    LDR R1, =__ctors_end__
ctor_loop:
    CMP R0, R1
    BEQ ctor_end
    LDR R2, [R0], #4
    STMFD SP!, {R0-R1}
    MOV LR, PC
    MOV PC, R2
    LDMFD SP!, {R0-R1}
    B ctor_loop
ctor_end:

 

a)g++ сгенерит табличку с адресами всех конструкторов глобальных статических объектов

B) линкер положит ее в секцию .ctors

c) ну а уж crt код вызовет по таблице все конструкторы.

e) все работает и мы счастливы

 

в сдучае с иаром скорее всего это уже сделано зарание.

 

упражнение для закрепления материала: аналогичными рассуждениями проработать вопрос деструкторы

контрольный вопрос; почему на деструктоы можно забить большой болт с дюймовой резбой?

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

/* .ctors .dtors are used for c++ constructors/destructors */
.ctors :
{
    PROVIDE(__ctors_start__ = .);
    KEEP(*(SORT(.ctors.*)))
    KEEP(*(.ctors))
    PROVIDE(__ctors_end__ = .);
} >ROM

 

Точняк! Только после перехода на eabi конструкторы хранятся в .init_array:

        __ctors_start__ = .;
        KEEP(SORT(*)(.init_array))
        __ctors_end__ = .;

Вот так точно работает.

 

2 inco. Ещё у меня был вариант с ключом -fdata-sections. Многие примеры загрузочных скриптов, имеющиеся в интернете это не учитывали. Надо добавить *(.bss.*) в bss:

    .bss :
    {
        . = ALIGN(4);
        _sbss = .;
         *(.bss)
         *(.bss.*)
...

Без этого глобальные объекты не инициализировались.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за информацию! Буду переваривать! Пока ничего не понял про конструкторы!

 

Объекты да глобальные.

 

По результатам отпишусь. Проверю только завтра на работе.

 

На всякий случай привожу свой скрипт линкера:

 

__Stack_Size = 1024 ;

 

PROVIDE ( _Stack_Size = __Stack_Size );

 

__Stack_Init = _estack - __Stack_Size;

 

/*"PROVIDE" allows to easily override these values from an object file or the commmand line.*/

PROVIDE ( _Stack_Init = __Stack_Init );

 

/*

There will be a link error if there is not this amount of RAM free at the end.

*/

_Minimum_Stack_Size = 0x100;

 

 

/* include the memory spaces definitions sub-script */

/*

Linker subscript for STM32F10x definitions with 512K Flash and 64K RAM */

 

/* Memory Spaces Definitions */

 

MEMORY

{

FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K

SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 48K

}

/* higher address of the user mode stack */

_estack = 0x2000c000;

 

 

 

/* include the sections management sub-script for FLASH mode */

 

/* Sections Definitions */

 

SECTIONS

{

/* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */

.isr_vector :

{

. = ALIGN(4);

KEEP(*(.isr_vector)) /* Startup code */

. = ALIGN(4);

} >SRAM

 

/* the program code is stored in the .text section, which goes to Flash */

.text :

{

. = ALIGN(4);

*(.text .text.* .gnu.linkonce.t.*)

*(.plt)

*(.gnu.warning)

*(.glue_7t) *(.glue_7) *(.vfp11_veneer)

*(.ARM.extab* .gnu.linkonce.armextab.*)

*(.gcc_except_table)

} >SRAM

 

.checksum 0x20005ffe :

{

. = ALIGN(2);

KEEP(*(.checksum)) /* Checksum code */

} >SRAM

 

.eh_frame_hdr : ALIGN (4)

{

KEEP (*(.eh_frame_hdr))

} >SRAM

.eh_frame : ALIGN (4)

{

KEEP (*(.eh_frame))

} >SRAM

/* .ARM.exidx is sorted, so has to go in its own output section. */

__exidx_start = .;

.ARM.exidx :

{

*(.ARM.exidx* .gnu.linkonce.armexidx.*)

} >SRAM

__exidx_end = .;

 

.rodata : ALIGN (4)

{

*(.rodata .rodata.* .gnu.linkonce.r.*)

 

. = ALIGN(4);

KEEP(*(.init))

 

. = ALIGN(4);

__preinit_array_start = .;

KEEP (*(.preinit_array))

__preinit_array_end = .;

 

. = ALIGN(4);

__init_array_start = .;

KEEP (*(SORT(.init_array.*)))

KEEP (*(.init_array))

__init_array_end = .;

 

. = ALIGN(4);

KEEP(*(.fini))

 

. = ALIGN(4);

__fini_array_start = .;

KEEP (*(.fini_array))

KEEP (*(SORT(.fini_array.*)))

__fini_array_end = .;

 

. = ALIGN(4);

KEEP (*crtbegin.o(.ctors))

KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))

KEEP (*(SORT(.ctors.*)))

KEEP (*crtend.o(.ctors))

 

. = ALIGN(4);

KEEP (*crtbegin.o(.dtors))

KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))

KEEP (*(SORT(.dtors.*)))

KEEP (*crtend.o(.dtors))

 

. = ALIGN(4);

_etext = .;

/* This is used by the startup in order to initialize the .data secion */

_sidata = _etext;

} >SRAM

 

/* This is the initialized data section

The program executes knowing that the data is in the RAM

but the loader puts the initial values in the FLASH (inidata).

It is one task of the startup to copy the initial values from FLASH to RAM. */

.data : AT ( _sidata )

{

. = ALIGN(4);

/* This is used by the startup in order to initialize the .data secion */

_sdata = . ;

KEEP(*(.jcr))

*(.got.plt) *(.got)

*(.shdata)

*(.data .data.* .gnu.linkonce.d.*)

 

. = ALIGN(4);

/* This is used by the startup in order to initialize the .data secion */

_edata = . ;

} >SRAM

 

/* This is the uninitialized data section */

.bss :

{

. = ALIGN(4);

/* This is used by the startup in order to initialize the .bss secion */

_sbss = .;

*(.shbss)

*(.bss .bss.* .gnu.linkonce.b.*)

*(COMMON)

. = ALIGN(4);

/* This is used by the startup in order to initialize the .bss secion */

_ebss = . ;

} >SRAM

 

PROVIDE ( end = _ebss );

PROVIDE ( _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 = . ;

} >SRAM

 

 

/* after that it's only debugging information. */

 

/* remove the debugging information from the standard libraries */

DISCARD :

{

libc.a ( * )

libm.a ( * )

libgcc.a ( * )

}

 

/* Stabs debugging sections. */

.stab 0 : { *(.stab) }

.stabstr 0 : { *(.stabstr) }

.stab.excl 0 : { *(.stab.excl) }

.stab.exclstr 0 : { *(.stab.exclstr) }

.stab.index 0 : { *(.stab.index) }

.stab.indexstr 0 : { *(.stab.indexstr) }

.comment 0 : { *(.comment) }

/* DWARF debug sections.

Symbols in the DWARF debugging sections are relative to the beginning

of the section so we begin them at 0. */

/* DWARF 1 */

.debug 0 : { *(.debug) }

.line 0 : { *(.line) }

/* GNU DWARF 1 extensions */

.debug_srcinfo 0 : { *(.debug_srcinfo) }

.debug_sfnames 0 : { *(.debug_sfnames) }

/* DWARF 1.1 and DWARF 2 */

.debug_aranges 0 : { *(.debug_aranges) }

.debug_pubnames 0 : { *(.debug_pubnames) }

/* DWARF 2 */

.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }

.debug_abbrev 0 : { *(.debug_abbrev) }

.debug_line 0 : { *(.debug_line) }

.debug_frame 0 : { *(.debug_frame) }

.debug_str 0 : { *(.debug_str) }

.debug_loc 0 : { *(.debug_loc) }

.debug_macinfo 0 : { *(.debug_macinfo) }

/* SGI/MIPS DWARF 2 extensions */

.debug_weaknames 0 : { *(.debug_weaknames) }

.debug_funcnames 0 : { *(.debug_funcnames) }

.debug_typenames 0 : { *(.debug_typenames) }

.debug_varnames 0 : { *(.debug_varnames) }

 

note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }

.ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }

/DISCARD/ : { *(.note.GNU-stack) }

}

 

Стартап брал стандартный из библиотеки stm32 версии 3.2

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Стартап брал стандартный из библиотеки stm32 версии 3.2

 

Этот стартап совершенно точно не вызывает конструкторы глобальных объектов.

Пример рабочего стартапа и линкерного скрипта для C++ можете посмотреть вот в этом примере из scmRTOS.

Проверено с kgp и codesourcery.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за помощь!!!

 

Всё получилось! Собрал гибрида стартапа из scmRtos и других примеров под себя и переделал скрипт линкера. Теперь вроде всё на месте, конструкторы работают. Вот только не понятно, как это всё работает, где бы про это почитать?

 

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

 

И ещё попутно вопрос по этой строке:

 

uart(const uint8_t n, const uint8_t c) : num(n), channel( c) { // Конструктор

 

параметры после двоеточия num(n), channel( c) не понимаю.

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

 

Если вопросы глупые прошу сильно не пинать, на плюсах первый раз решил написать прогу.

До этого всё больше на чистом си. А теперь понравилось! Оказывается такая вещь... :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Всё получилось!

Аминь:)

Интересует как происходит процесс инициализации конструкторов в деталях.

При помощи вызова оных:) Аналогия с си - прямая. Если в си есть статическая переменная, то она инициализируется в стартапе. Если в C++ есть глобальный объект, то он точно так же инициализируется в стартапе. За инициализацию отвечает конструктор. Поэтому он вызывается из стартапа.

Вообще где почитать

Читать естественно "Язык программирования С++", Бьерн Страуструп. Третье издание.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При помощи вызова оных:) Аналогия с си - прямая. Если в си есть статическая переменная, то она инициализируется в стартапе. Если в C++ есть глобальный объект, то он точно так же инициализируется в стартапе. За инициализацию отвечает конструктор. Поэтому он вызывается из стартапа.
Более "на пальцах": Указатели на конструкторы помещаются в секцию .ctors, а код в стартапе проходит эту секцию, вызывая функции по считанному указателю. Если указатель равен нулю - баста. "Ловкость рук и никакого мошенничества".

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо просветили! Скачал третье издание Страуструпа, все непонятности там нашёл! Да! Мир сильно изменился за это время. Тот что у меня на полке был (второе издание от 1993 года) там этого не было! Теперь на счёт языка всё стало ясно!

 

Теперь возникла другая проблема. Опять где-то, что-то не склеилось. В озу программа заработала полностью, а вот в пзу не хочет. Линкер разместил какие-то константы по адресу озу и J-Flash ARM не хочет шить пзу говорит недопустимый сегмент. Посмотрел мап файл, да действительно он туда распределил секцию дата:

.data 0x20000000 0x18

0x20000000 . = ALIGN (0x4)

0x20000000 _sdata = .

Вот только какого хрена она попадает в бинарный файл (который из-за этого получается размером с пол гиктара) пока не пойму. С кодом для озу при этом всё в порядке и бинарник размером 13К.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Линкер разместил какие-то константы по адресу озу и J-Flash ARM не хочет шить пзу говорит недопустимый сегмент. Посмотрел мап файл, да действительно он туда распределил секцию дата:

.data 0x20000000 0x18

0x20000000 . = ALIGN (0x4)

0x20000000 _sdata = .

Вот только какого хрена она попадает в бинарный файл (который из-за этого получается размером с пол гиктара) пока не пойму.

Можно предположить, что некорректно написан скрипт линкера. Дело в том, что по стандартам gcc секция .data - инициализированные константы. Для упрощения процесса инициализации копия всех инициализированных значений хранится во флеше и при старте программы просто "тупо копируется" в ОЗУ. Для корректного выполнения этой операции скрипт линкера должен выглядеть примерно так:
  /* .data section which is used for initialized data */
  .data : /* place init values immediatly after .text section */
  {
    _data = .;
    *(.ramfunc*)
    
    *(.data*)

    _edata = .;
    _data_image = LOADADDR(.data);

    PROVIDE (edata = .);
  } > RAM  AT > ROM

То есть разместить выходную секцию .data в регион RAM (Virtual Memory Address, VMA), но их значения положить в регион ROM (в Loadable Memory Address, LMA). В вашем скрипте магические слова "AT > ROM" скорее всего отсутствуют, и начальные значения остаются на своих реальных адресах.

 

P.S. И старайтесь оформлять исходники используя кнопку rte-code-button.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Более "на пальцах": Указатели на конструкторы помещаются в секцию .ctors

В секцию .init_array! :maniac: :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо ещё раз!!!

 

Дали направление поиска.

 

Вот выкладываю свой стартап и скрипт линкера может глянете, если не затруднит!

А то сразу въехать в линкерный скрипт тяжело.

 

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

link_startup.zip

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот выкладываю свой стартап и скрипт линкера может глянете, если не затруднит!

 

    .text.align :
    {
        . = ALIGN(8);
        _etext = .;
        _sidata = _etext;        /* start of initialized data label */
    } >RAM

Вот это надо вернуть во FLASH. (Как и было в исходном скрипте из scmRTOS :) )

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

    .text.align :
     {
         . = ALIGN(8);
         _etext = .;
         _sidata = _etext;        /* start of initialized data label */
     } >RAM

Вот это надо вернуть во FLASH. (Как и было в исходном скрипте из scmRTOS :) )

 

Да это было оно! Всё работает! Ура !!! И во флеше и в озу!

Почему-то подумал что этот сегмент данных надо размещать в озу.

 

СПАСИБО !!!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...