Jump to content

    

GCC, размещение переменной по фиксированному адресу

Захотелось мне разместить переменную в самом конце используемой области флеш - после секций .text и .data.

 

И я не придумал ничего лучшего, как в скрипте линкера написать

/*выше - секция .data */

    /* конец данных во флеш - кладём сюда контрольную сумму */
    .CheckSum :
        /*AT ( _sidata + SIZEOF ( .data ) )*/
        AT ( _sidata + (_edata - _sdata) )
    {
        . = ALIGN(4);
        KEEP(*(.CheckSum))
    } > FLASH

/*ниже - секция .bss*/

(пробовал оба варианта AT(), работает одинаково)

 

__attribute__ ((section(".CheckSum")))
const uint32_t gCoreChksum = 0xAABBCCDD;

 

 

И получил следующее:

- в map-файле моя переменная имеет тот же адрес, что и начало .data

- в программе взятие адреса также возвращает адрес начала .data

- в скомпилированном бинарнике переменную явно видно в самом конце, как я и хотел. Адрес её при этом с не совпадает с тем, что написано в map'е.

 

Собственно, вопросы:

 

- я правильно понимаю, что нашёл баг в линкере?

C:\Program Files\GNU Tools ARM Embedded\4.9 2015q1\bin>arm-none-eabi-ld.exe --version
GNU ld (GNU Tools for ARM Embedded Processors) 2.24.0.20150304

По хорошему, он должен работать единообразно. Или же единообразно не работать :-)

 

- (более насущный) как по-человечески сделать?

 

UPDATE

Вот так работает:

    /* конец данных во флеш - кладём сюда контрольную сумму */
    .CheckSum  (_sidata + SIZEOF(.data)) :
    {
        . = ALIGN(4);
        KEEP(*(.CheckSum))
    } > FLASH

 

Плохо, что я никак концепцию этого странного скрипта не пойму...

Share this post


Link to post
Share on other sites

Вы можете написать просто

    /* конец данных во флеш - кладём сюда контрольную сумму */
    .text.CheckSum :
    {
        . = ALIGN(4);
        KEEP(*(.CheckSum))
    } > FLASH

И оно ляжет вслед за всем предыдущим содержанием региона FLASH. Причем во избежание неприятных сюрпризов (типа выкидывания этой секции при генерации .hex) имя этой секции должно начинаться с .text.

 

А еще можно там же определить символ конца занятой памяти (для расчета этой самой контрольной суммы в программе):

    /* конец данных во флеш - кладём сюда контрольную сумму */
    .text.CheckSum :
    {
        . = ALIGN(4);
        KEEP(*(.CheckSum))
        _image_end = .;
    } > FLASH

 

 

И еще - смею предположить, что в объявлении секций .bss и .data у вас тоже прописаны эти совершенно лишние AT(). Положитесь на линкер, он сам умеет размещять секции подряд:

    .data :
    {
        . = ALIGN(4);
        _sdata = .;                /* start of .data label */
        *(.ramfunc)
        *(.ramfunc.*)
        *(.data)
        *(.data.*)
        . = ALIGN(4);
        _edata = .;                /* end of .data label */
    } > RAM AT > FLASH
    _sidata = LOADADDR(.data);    /* start of initialized data label */
    .bss (NOLOAD):
    {
        . = 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

Share this post


Link to post
Share on other sites

Лучше тему перенести в раздел "opensource".

Share this post


Link to post
Share on other sites

Сергей, спасибо.

Корректно Ваш пример заработал только после изъятия AT() у секции .data

Без этого линкер пытался наложить .data и .CheckSum и падал с ошибкой.

 

 

 

А скрипт с "совершенно лишними AT()" написал не я.

Вот он, виновник :-)

 

//*     FULLNAME:  Single-Chip Microcontroller Real-Time Operating System
//*     NICKNAME:  scmRTOS
//*     PROCESSOR: ARM Cortex-M3 
//*     TOOLKIT:   ARM GCC
//*     PURPOSE:   Port Test File
//*     gcc port by Anton B. Gusev aka AHTOXA, Copyright (c) 2009-2012

 

/*************************************************
* linker script for STM32F10x Connectivity Line Devices
************************************************/ 

ENTRY(Reset_Handler)

_Minimum_Stack_Size = 0x100 ;

MEMORY
{
RAM (xrw)	: ORIGIN = 0x20000000, LENGTH =  64K
FLASH (rx)	: ORIGIN = 0x08000000, LENGTH = 256K
}

/* higher address of the user mode stack */
PROVIDE ( _estack = ALIGN(ORIGIN(RAM) + LENGTH(RAM) - 8 ,8) );

SECTIONS
{
.isr_vector :
{
	. = ALIGN(4);
	KEEP(*(.isr_vector))
	. = ALIGN(4);
} > FLASH

.text :
{
	__ctors_start__ = .;
	KEEP(SORT(*)(.init_array))  /* eabi uses .init_array for static constructor lists */
	__ctors_end__ = .;

	__dtors_start__ = .;
	__dtors_end__ = .;

	. = ALIGN(4);
	*(.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);
} > FLASH

/* .ARM.exidx is sorted, so has to go in its own output section.  */
__exidx_start = .;
.ARM.exidx :
{
	*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;

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

.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

/*
* 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


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 ( * )
}

/* 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) }
}

 

Share this post


Link to post
Share on other sites
А скрипт с "совершенно лишними AT()" написал не я.

Вот он, виновник :-)

У виновника "AT" - это правильный, нужный AT:-)

И тот, который вы убрали - он тоже нужен. Это собственно один и тот же AT, просто указанный разными способами.

        ...
        _sidata = _etext;      /* засекли адрес во флеш (LMA) */
    } > FLASH
    .data : AT ( _sidata )        /* Указываем, что образ секции .data (инициализированные данные) */
    {                        /* лежит во флеше начиная с адреса _sidata.  */
        ...                    /* Потом стартовый код скопирует их в RAM.  */ 
    } > RAM

То же самое можно написать иначе:

    .data :
    {                        /* Образ секции .data будет во флеш сразу за предыдущей секцией */
        ...
    } > RAM AT > FLASH

У вас без этого AT инициализированные переменные должны перестать инициализироваться.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this