Jump to content
    

Гарантированно расположить секцию самой последней в регионе памяти.

Вопрос знатокам GCC и в частности - его компоновщика ld:

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

Сделал скрипт:

ENTRY(AppStart)

MEMORY
{
  RAM_regionA (rx)   : ORIGIN = 0x20000000, LENGTH = 0x020000 /* DTCM  (code) */
  RAM_regionB (rwx)  : ORIGIN = 0x20020000, LENGTH = 0x020000 /* SRAM1 (data)  */
  RAM_regionC (rx)   : ORIGIN = 0x20040000, LENGTH = 0x03C000 /* SRAM1 (data)  */
  RAM_regionD (rwx)  : ORIGIN = 0x2007C000, LENGTH = 0x004000 /* SRAM2         */
}

SECTIONS
{
  .text : {
    . = ALIGN(4);
    __checksum_begin = .;
    KEEP(*(.intvkt))
    __checksum = .;
    KEEP(*(.checksum))
    KEEP(*(.codehead))
    KEEP(*(.intvktTail))
    KEEP(*(.codeSignature))
    *(.text*)
    *(.rodata*)
    *(.glue_7)
    *(.glue_7t)
    . = ALIGN(4);
    _sfb_idata = .;
    . = ALIGN(4);
    KEEP(*(.codeKeys))
  } > RAM_regionA

  ...

  .codetail : {
    KEEP(*(.codetail))
    __checksum_end = .;
  } > RAM_regionA
}

где на месте многоточия определено ещё множество других выходных секций. Требуется чтобы .codetail была гарантированно последней в регионе RAM_regionA.

Сейчас я вижу, что она идёт последней среди секций описанных в скрипте:

.codetail       0x200072e8       0x10
 *(.codetail)
 .codetail      0x200072e8       0x10 B:/UPDATER.GCC/OUT.OUT/misca.obj
                0x200072e8                codetail
                0x200072f8                        __checksum_end = .

но... затем, в этот же регион ld вставляет ряд стабов:

LOAD linker stubs

.raw            0x200072f8        0x0
 .raw           0x200072f8        0x0 B:/UPDATER.GCC/OUT.OUT/misca.obj

.ARM.extab      0x200072f8        0x0
 .ARM.extab     0x200072f8        0x0 C:/PRG/GCC/ARM/lib/gcc/arm-none-eabi/14.2.1/thumb/v7e-m+dp/hard/libg.a(libc_a-memcpy.o)
 .ARM.extab     0x200072f8        0x0 C:/PRG/GCC/ARM/lib/gcc/arm-none-eabi/14.2.1/thumb/v7e-m+dp/hard/libg.a(libc_a-strlen.o)

.ARM.exidx      0x200072f8        0x8
 .ARM.exidx     0x200072f8        0x8 C:/PRG/GCC/ARM/lib/gcc/arm-none-eabi/14.2.1/thumb/v7e-m+dp/hard/libg.a(libc_a-memcpy.o)
 .ARM.exidx     0x20007300        0x0 C:/PRG/GCC/ARM/lib/gcc/arm-none-eabi/14.2.1/thumb/v7e-m+dp/hard/libg.a(libc_a-strlen.o)
                                  0x8 (size before relaxing)

.ARM.extab.text.__udivmoddi4
                0x20007300        0x0
 .ARM.extab.text.__udivmoddi4
                0x20007300        0x0 C:/PRG/GCC/ARM/lib/gcc/arm-none-eabi/14.2.1/thumb/v7e-m+dp/hard/libgcc.a(_udivmoddi4.o)

.ARM.exidx.text.__udivmoddi4
                0x20007300        0x0
 .ARM.exidx.text.__udivmoddi4
                0x20007300        0x0 C:/PRG/GCC/ARM/lib/gcc/arm-none-eabi/14.2.1/thumb/v7e-m+dp/hard/libgcc.a(_udivmoddi4.o)
                                  0x8 (size before relaxing)

.rel.dyn        0x20007300        0x0
 .rel.iplt      0x20007300        0x0 B:/UPDATER.GCC/OUT.OUT/aes256.obj

и .codetail становится уже не самой последней.  :sad:

 

Как сделать, чтобы все сгенерённые компоновщиком секции, идущие в данный регион, также вставлялись выше .codetail?

В скрипте имеются также и другие описанные регионы памяти, в которые идёт компоновка других секций.

 

PS: В IAR это решается элементарно: достаточно указать "last section .codetail". Неужели в GCC ld подобной возможности нет?

Share this post


Link to post
Share on other sites

On 4/15/2025 at 2:39 PM, jcxz said:

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

Секции размещаются в памяти в той последовательности, в которой они описаны в скрипте компоновщика.

On 4/15/2025 at 2:39 PM, jcxz said:

 

но... затем, в этот же регион ld вставляет ряд стабов:

Возможно у вас стабы не описаны в вашем скрипте и поэтому линковщик сам их пихает по своему разумению.
Опишите их у себя.

 

Spoiler
/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20020000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x800;      /* required amount of heap  */
_Min_Stack_Size = 0x800; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
RAM (xrw)      		: ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (rw)      	: ORIGIN = 0x10000000, LENGTH = 64K
SETTINGS_FLASH (rx)	: ORIGIN = 0x08004000, LENGTH = 16K
SERVICE_FLASH (rx) 	: ORIGIN = 0x08020000, LENGTH = 2K
FLASH (rx)      	: ORIGIN = 0x08020800, LENGTH = 448K
}

/* Define output sections */
SECTIONS
{
  .settings (NOLOAD):
  {
  	. = ALIGN(2048);
  	_settings = .;
  	KEEP(*(.settings))           
    KEEP(*(.settings*))
    . = ALIGN(2048);
  } >SETTINGS_FLASH

  /* The startup code goes first into FLASH */
  .isr_vector (READONLY) :
  {
    . = ALIGN(4);
    _image_start = .;
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >SERVICE_FLASH
  
   .text_len :
  {
  	. = ALIGN(8);
  	_text_len = .;
  	KEEP(*(.text_len))           
    KEEP(*(.text_len*))
    LONG((_image_end - _image_start) / 4);    /* application size, in 4-byte words */
    . = ALIGN(8);
  } >SERVICE_FLASH  
  
  .version (READONLY) :
  {
  	. = ALIGN(8);
  	_version = .;
  	KEEP(*(.version))           
    KEEP(*(.version*))
    . = ALIGN(8);
  } >SERVICE_FLASH
  
  .b_data (READONLY) :
  {
  	. = ALIGN(8);
  	_b_data = .;
  	KEEP(*(.b_data))           
    KEEP(*(.b_data*))
    . = ALIGN(8);
  } >SERVICE_FLASH
  
  .b_version (READONLY) :
  {
  	. = ALIGN(8);
  	_b_data = .;
  	KEEP(*(.b_version))           
    KEEP(*(.b_version*))
    . = ALIGN(8);
  } >SERVICE_FLASH

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(8);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(8);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(8);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(8);
  } >FLASH

  .ARM.extab : 
  { 
  	. = ALIGN(8);
  	*(.ARM.extab* .gnu.linkonce.armextab.*) 
  	. = ALIGN(8);
  } >FLASH
  
  .ARM : 
  {
  	. = ALIGN(8);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(8);
  } >FLASH

  .preinit_array :
  {
  	. = ALIGN(8);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(8);
  } >FLASH
  
  .init_array :
  {
  	. = ALIGN(8);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(8);
  } >FLASH
  
  .fini_array :
  {
  	. = ALIGN(8);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(8);
  } >FLASH
  

  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : 
  {
    . = ALIGN(8);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(8);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> FLASH

  _siccmram = LOADADDR(.ccmram);

  /* CCM-RAM section 
  * 
  * IMPORTANT NOTE! 
  * If initialized variables will be placed in this section,
  * the startup code needs to be modified to copy the init-values.  
  */
  .ccmram :
  {
    . = ALIGN(8);
    _sccmram = .;       /* create a global symbol at ccmram start */
    *(.ccmram)
    *(.ccmram*)
    
    . = ALIGN(8);
    _eccmram = .;       /* create a global symbol at ccmram end */
  } >CCMRAM AT> FLASH

  .crc (READONLY) :
  {
  	KEEP(*(.crc))           
    KEEP(*(.crc*))
    _image_end = .;
  } >FLASH
  
  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM

  

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

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

 

 

Share this post


Link to post
Share on other sites

.подправил предыдущее сообщение

Share this post


Link to post
Share on other sites

2 часа назад, dimka76 сказал:

Возможно у вас стабы не описаны в вашем скрипте и поэтому линковщик сам их пихает по своему разумению.
Опишите их у себя.

Я предполагал такой ответ... А откуда я знаю - какие имена стабов и veener-ов может сгенерить компоновщик или компилятор? Пускай даже сейчас я могу по .map-у посмотреть. Но завтра что-то добавлю в проект или новая версия GCC выйдет и нужно каждый раз проверять - не появилось ли новых секций с неизвестными ранее именами?  :dash2:

Нужно - чтобы все секции с неизвестными именами (не фигурирующими явно нигде в скрипте), но подходящие по атрибутам (ro, rxo, ...) попадали в определённую секцию. Туда где "...". А .codetail - была после них. 

В IAR такое делается элементарно. Неужто во всеми хвалимом GCC такой ерунды нет???  :mda:

Share this post


Link to post
Share on other sites

On 4/15/2025 at 5:31 PM, jcxz said:

Нужно - чтобы все секции с неизвестными именами (не фигурирующими явно нигде в скрипте), но подходящие по атрибутам (ro, rxo, ...) попадали в определённую секцию. Туда где "...". А .codetail - была после них. 

Судя по примеру из вашего первого поста, вам надо найти до куда считать CRC.

В GCC есть стандартные и непоколебимые имена секций text, data, bss
Все эти стабы попадают между секциями text и data. Конец вашей прошивки это конец секции data.
Вот и размещайте вашу секцию 

.codetail 

после секции data.

On 4/15/2025 at 5:31 PM, jcxz said:

В IAR такое делается элементарно. Неужто во всеми хвалимом GCC такой ерунды нет???  :mda:

У всех компиляторов есть свои преимущества и недостатки.

Share this post


Link to post
Share on other sites

4 часа назад, dimka76 сказал:

Судя по примеру из вашего первого поста, вам надо найти до куда считать CRC.

Не только. Мне нужно найти границы RO-образа прошивки.

4 часа назад, dimka76 сказал:

В GCC есть стандартные и непоколебимые имена секций text, data, bss
Все эти стабы попадают между секциями text и data. Конец вашей прошивки это конец секции data.
Вот и размещайте вашу секцию 

.codetail 

после секции data.

Серьёзно??? :shok:  А ничего что .data - это RW-секция???  А значит - она никак не может быть "концом прошивки".

Если же вы имели в виду .rodata, то советую ещё раз и внимательнее перечитать мой первый пост. Особенно обратить внимание на приведённые там результаты компоновки.

Share this post


Link to post
Share on other sites

16 hours ago, jcxz said:

Требуется чтобы .codetail была гарантированно последней в регионе RAM_regionA.

Включите для этой секции выравнивание размеров с секцию.

Share this post


Link to post
Share on other sites

On 4/16/2025 at 3:59 AM, jcxz said:

Не только. Мне нужно найти границы RO-образа прошивки.

Серьёзно??? :shok:  А ничего что .data - это RW-секция???  А значит - она никак не может быть "концом прошивки".

Нет, в секции .data хранятся значения для инициализируемых переменных.
Таким образом, инициализируемые переменные занимают две области памяти: RO для исходных значений и RW для собственно самих переменных.

.bss секция это как раз чисто RW. Это глобальные переменные, которые явно не проинициализированы и которые инициализируются нулями.

И секции .ARM.extab, .ARM,  .fini, .fini_array и прочие, которые присутствуют в приведенном мною выше скрипте, это не случайные секции и они есть и будут всегда и компилятор вам

ничего больше лишнего не напихает, кроме как необходимых секций.

 

Вот тут есть кое что про секции

https://stackoverflow.com/questions/40532180/understanding-the-linkerscript-for-an-arm-cortex-m-microcontroller

Share this post


Link to post
Share on other sites

5 часов назад, tonyk_av сказал:

Включите для этой секции выравнивание размеров с секцию.

Уже давно включено - ничего не изменилось. Да и как может помочь выравнивание?

3 часа назад, dimka76 сказал:

Нет, в секции .data хранятся значения для инициализируемых переменных.

Серьёзно??? Что это за такая уникальная версия GCC у вас, у которой в .data хранятся инициализаторы переменных?  :shok:

В своём GCC по .map-у (и другим файлам) вижу, что .data - это RW-секция с инициализированными (не 0-и) данными. А инициализаторы - в .rodata. Да и в инете во всех примерах это видно.

3 часа назад, dimka76 сказал:

.bss секция это как раз чисто RW. Это глобальные переменные, которые явно не проинициализированы и которые инициализируются нулями.

Азы мне не нужно рассказывать.  :smile:

3 часа назад, dimka76 сказал:

И секции .ARM.extab, .ARM,  .fini, .fini_array и прочие, которые присутствуют в приведенном мною выше скрипте, это не случайные секции и они есть и будут всегда и компилятор вам

ничего больше лишнего не напихает

Уверены? Откуда такая уверенность???

Просматривая множество примеров в инете, встречал и другие названия автогенерируемых GCC секций. А какие автогенерируемые секции появятся в следующих версиях GCC?

3 часа назад, dimka76 сказал:

Вы бы хоть свои же ссылки читали. :punish: Читаем написанное там вслух:

Цитата

The data section is put into flash. Why is that interesting? Because the .data sections are read/write. They're put into flash so that they are there when the power is turned on. The startup code copies them to RAM.

А подобного я уже массу в инетике перечитал. Удовлетворительного ответа не нашёл. После чего и создал тему.

Share this post


Link to post
Share on other sites

On 4/16/2025 at 12:42 PM, jcxz said:

Серьёзно??? Что это за такая уникальная версия GCC у вас, у которой в .data хранятся инициализаторы переменных?  :shok:

В своём GCC по .map-у (и другим файлам) вижу, что .data - это RW-секция с инициализированными (не 0-и) данными. А инициализаторы - в .rodata. Да и в инете во всех примерах это видно.

 

Ладно, инициализаторы хранятся не в .data, но и не в .rodata.
Смотрим скрипт линкера.
 

 /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(8);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(8);
  } >FLASH
    
    
.fini_array :
  {
  	. = ALIGN(8);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(8);
  } >FLASH  

  /* used by the startup to initialize data */
  
    //----->>> ОБРАТИТЕ ВНИМАНИЕ НА ЭТУ СТРОЧКУ !!!
  
  _sidata = LOADADDR(.data); 

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : 
  {
    . = ALIGN(8);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
    . = ALIGN(8);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> FLASH

  .crc :
  {
  	KEEP(*(.crc))           
    KEEP(*(.crc*))
    _image_end = .;
  } >FLASH

Теперь смотрим startup

void __attribute__((naked, noreturn)) Reset_Handler()
{
	unsigned long *pulSrc, *pulDest;

	pulSrc = &_sidata;

	for(pulDest = &_sdata; pulDest < &_edata; )
		*(pulDest++) = *(pulSrc++);

	for(pulDest = &_sbss; pulDest < &_ebss; )
		*(pulDest++) = 0;

	SystemInit();
	__libc_init_array();
	(void)main();
	for (;;) ;
}

И смотрим откуда и куда копируются инициализаторы.
Т.о. я описал секцию .crc после секции .data и этим гарантировал, что _image_end указывает на конец "прошивки".

Еще приведу выдержку из map файла

 .rodata.main.str1.1
                0x08007c17      0x1c1 release/obj/main.o
                                0x1c4 (size before relaxing)
                0x08007dd8                . = ALIGN (0x8)

.ARM.extab      0x08007dd8        0x0  
  ........................
.preinit_array  0x08007de0        0x0 
  ........................................
.init_array     0x08007de0        0x0
  .....................................
.fini_array     0x08007de0        0x0  
 ................................................
                0x08007de0                _sidata = LOADADDR (.data)
 ......................................
   
.crc            0x08008248        0x4
   ...............................................
                0x0800824c                _image_end = .

Здесь видно где заканчивается секция .rodata, на что указывает _sidata, где начинается завершающая секция .crc и на что указывает _image_end

Share this post


Link to post
Share on other sites

23 hours ago, jcxz said:

Как сделать, чтобы все сгенерённые компоновщиком секции, идущие в данный регион, также вставлялись выше .codetail?

попрорбуйте принудительно переместить указатель в конец

Quote

  .codetail : {
    KEEP(*(.codetail))
    __checksum_end = .;
    . = ORIGIN(RAM_regionA) + LENGTH(RAM_regionA);
  } > RAM_regionA

 

Share this post


Link to post
Share on other sites

2 hours ago, jcxz said:

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

Это они?

  .ARM.extab   : { 
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >SECTOR_5_7

 

Share this post


Link to post
Share on other sites

On 4/16/2025 at 2:39 PM, sasamy said:

попрорбуйте принудительно переместить указатель в конец

Не получится
LENGHT() вернет размер всего региона памяти, а не размер занятой памяти.

Quote

LENGTH(memory)

Return the length of the memory region named memory

 

Share this post


Link to post
Share on other sites

2 часа назад, sasamy сказал:

попрорбуйте принудительно переместить указатель в конец

Что это даст? Зачем перемещать его за границу памяти?

2 часа назад, tonyk_av сказал:

Это они?

  .ARM.extab   : { 
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >SECTOR_5_7

И такие. И другие встречал.

Share this post


Link to post
Share on other sites

В 15.04.2025 в 14:39, jcxz сказал:

PS: В IAR это решается элементарно

Если не секрет, почему тогда отказались от ИАРа?  Мне в свое время пришлось, т.к. заказчик требовал лиц. чистоту, но не хотел платить 3000$ за лицензию, даже в складчину со мной, ну времена еще были не те))) Так бы никогда с ИАРа не съехал - он гораздо удобнее...

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...