Understanding Linkerscene for ARM Cortex-M Microcontroller

I am using the STM32F746NG microcontroller from STMicroelectronics. This device is based on the ARM Cortex-M7 architecture. I spent quite a bit of time understanding linkerscript from sample projects. I understood the basics, but I still can not understand the big parts. Please help me understand these parts.

Linker Start

Linkestra begins as follows:

/* Entry Point */ ENTRY(Reset_Handler) /* The function named 'Reset_Handler' is defined */ /* in the 'startup.s' assembly file. */ /* Highest address of the user mode stack */ /* Remember: the stack points downwards */ _estack = 0x20050000; /* End of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ _Min_Heap_Size = 0x200; /* Required amount of heap */ _Min_Stack_Size = 0x400; /* Required amount of stack */ /* --------------------------------------------------------------------*/ /* MEMORY AREAS */ /* --------------------------------------------------------------------*/ MEMORY { /* FLASH MEMORY */ /* ------------ */ /* Remember: the flash memory on this device can */ /* get accessed through either the AXIM bus or the */ /* ITCM bus. Accesses on the ITCM bus start at */ /* address 0x0020 0000. Accesses on the AXIM bus */ /* at address 0x0800 0000. */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K /* FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 1024K */ /* RAM MEMORY */ /* ---------- */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K } 

Vector table and program code

After defining memory areas, the linkstream continues to define sections. The first section defined in the linker is a vector table. It should end in the first bytes of flash memory.

 /* --------------------------------------------------------------------*/ /* OUTPUT SECTIONS */ /* --------------------------------------------------------------------*/ SECTIONS { /****************************/ /* VECTOR TABLE */ /****************************/ .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* Vector Table */ . = ALIGN(4); } >FLASH 

After the vector table is inserted, this is the time for the program code:

  /****************************/ /* PROGRAM CODE */ /****************************/ .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ *(.glue_7) /* Glue ARM to Thumb code */ *(.glue_7t) /* Glue Thumb to ARM code */ *(.eh_frame) /* Note: The function '.text.Reset_Handler' is one of the *(.text*) sections, */ /* such that it gets linked into the output .text section somewhere here. */ /* We can verify the exact spot where the Reset_Handler section is positioned, by */ /* examining the second entry of the vector table. */ /* A test has given the following results: /* FLASH (rx) : ORIGIN = 0x0800 0000 ==> Reset_Handler = 0x0800 1C91 */ /* FLASH (rx) : ORIGIN = 0x0020 0000 ==> Reset_Handler = 0x0020 1CB9 */ /* /* In both cases, the Reset_Handler section ends up a few hundred bytes after the */ /* vector table in Flash. But in the first case, the "Reset_Handler" symbol points */ /* to the Reset-code through AXIM-interface, whereas in the latter case it points */ /* to the Reset-code through the ITCM-interface. */ KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; /* Define a global symbol at end of code */ } >FLASH 

Linkerscript defines a global e_text character that represents the address where program code ends in flash memory.

Persistent data

Read-only data also ends up in flash memory (it makes no sense to put it in RAM, which is volatile). Linkerscript determines that the .rodata section must be in flash:

  /****************************/ /* CONSTANT DATA */ /****************************/ .rodata : { . = ALIGN(4); *(.rodata) /* .rodata sections (constants, strings, etc.) */ *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ . = ALIGN(4); } >FLASH 

Mysterious sections in flash memory

After determining where read-only permanent data should go, the link script determines that several "mysterious" partitions should also end in flash memory:

  .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH .ARM : { __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; } >FLASH .preinit_array : { PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); } >FLASH .init_array : { PROVIDE_HIDDEN (__init_array_start = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); } >FLASH .fini_array : { PROVIDE_HIDDEN (__fini_array_start = .); KEEP (*(SORT(.fini_array.*))) KEEP (*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); } >FLASH 

I have no idea what these sections are. Therefore, let this be the first question. What are these sections and in which object files do they appear? As you know, linker script needs to link some object files. I do not know in what object files these mysterious sections exist:

  • .ARM.extab
  • .ARM
  • .preinit_array
  • .init_array
  • .fini_array

This is the end of the flash allocation. Linkerscript continues with defining sections that end in RAM.

Sections in RAM

The .data and .bss sections are clear to me. No questions about this.

  /****************************/ /* INITIALIZED DATA */ /****************************/ _sidata = LOADADDR(.data); .data : { . = ALIGN(4); _sdata = .; /* create a global symbol at data start */ *(.data) /* .data sections */ *(.data*) /* .data* sections */ . = ALIGN(4); _edata = .; /* define a global symbol at data end */ } >RAM AT> FLASH /****************************/ /* UNINITIALIZED DATA */ /****************************/ . = ALIGN(4); .bss : { _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 

Linkerscript also defines the ._user_heap_stack section:

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

This section does not appear to be used immediately. It is determined only to check if RAM has enough space for the stack and heap. Linker error occurs when it is not ( . Exceeds the upper RAM address).

Linker End

Here's how the linker ends. And honestly, I have no idea what he is doing. So this is the second question: what does the following mean?

  /* Remove information from the standard libraries */ /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } .ARM.attributes 0 : { *(.ARM.attributes) } } /* END OF LINKERSCRIPT */ 
+5
source share
2 answers
  • .ARM.extab and .ARM.exidx are associated with unwinding. You can find more information here http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/index.html . You do not need them if you are not worried about unwinding (unwinding is useful for eliminating C ++ and for debugging).

  • These characters are associated with the C / C ++ constructor and the launch of the destructor and reset the code that is called before / after main (). Sections with the names .init, .ctors, .preinit_array and .init_array are associated with the initialization of C / C ++ objects, and the sections .fini, .fini_array and .dtors are for breaking. Start and end characters define the start and end of code sections associated with such operations, and can refer to other parts of the runtime support code.

The sections .preinit_array and .init_array contain arrays of pointers to functions that will be called upon initialization .. fini_array is an array of functions that will be called upon destruction. Presumably, start and end labels are used for these lists.

  1. heap: in fact, this part does not allow you to reserve a place for the heap and some space for the stack. Obviously, it causes an error if the sum of the reserved areas goes beyond the boundaries of RAM. This is an example:

    _Min_Heap_Size = 0; / * required amount of heap / _Min_Stack_Size = 0x400; / required amount of stack * /

    ._ user_heap_stack: {, = ALIGN (4); PROVIDE (end =.); PROVIDE (_end =.) ;, the sign is + _Min_Heap_Size ;, the sign is + _Min_Stack_Size ;, = ALIGN (4); }> RAM

  2. to link libraries, I prefer a different notation, this is just an example for a simple project without RTOS C ++: GROUP (libgcc.a libc_nano.a libstdC ++ _ nano.a libm.a libcr_newlib_nohost.a crti.o crtn.o crtbegin .o crtend.o)

+2
source

First of all, your approach to understanding this concept is incorrect; that's what i think. I encountered a similar problem while understanding this concept.

In simple language, I can explain to you that the linker script provides us with three main things:

  • Point of entry
  • Backup addresses in main memory.
  • Copy down procedure

So, let's look at one example:

Suppose we have N number of .c files in our project. Now, after compilation, each file contains its own translation units, called object files.

Each object file contains a section / segment .text containing the actual code. Section .data / segment for data.

To combine all the .text sections of each translation unit, the linker script provides some specific commands for this. This is the same for the .data section.

After combining all the object files, the final executable file is ready for use.

Now let's move on to the paradox ...

The entry point for the Cortex-M series is ResetISR only. After executing the ResetISR function and initializing other masked interrupts in the SoC, the next step is the copy procedure.

The copy procedure is nothing more than copying the .data section to RAM (including even the interesting .bss section, but I'm not considering this part right now).

Copying from ROM to RAM is necessary because the actual ELF file is always stored in ROM.

After doing all these launch-related things, we can now call our main() function.

0
source

Source: https://habr.com/ru/post/1259551/


All Articles