13

I am using the STM32F746NG microcontroller from STMicroelectronics. This device is based on the ARM Cortex-M7 architecture. I invested quite some time in understanding the linkerscript from example projects. I figured out the basics, but I still cannot grasp big parts of it. Please help me to understand those parts.

Start of the linkerscript

The linkerscript starts 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 the memory areas, the linkerscript proceeds with defining the sections. The first section defined in the linkerscript is the vector table. It has to end up in the first bytes of the 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, it's 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

The linkerscript defines the e_text global symbol that represents the address where the program code in flash ends.

Constant data

The read-only data ends up in the flash memory as well (it makes no sense to put it in RAM, which is volatile). The linkerscript defines that the .rodata section should 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

After defining where the constant read-only data should go, the linkerscript defines that a few 'mysterious' sections should end up in flash as well:

    .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 those sections are. So let this be the first question. What are these sections, and in what object files do they show up? As you know, the linkerscript needs to link together some object files. I have no idea in what object files these mysterious sections exist:

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

This is the end of allocations to the flash memory. The linkerscript continues with defining sections that end up in the 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

The linkerscript defines also a ._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

Apparently this section is not used immediately. It is only defined to check if the RAM has still enough space for the stack and the heap. A linker error is thrown when this is not the case (the . exceeds the top RAM address).

The end of the linkerscript

This is how the linkerscript ends. And honestly, I have no idea what it does. 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 */
4

2 回答 2

9
  1. .ARM.extab 和 .ARM.exidx 与展开有关。您可以在此处找到更多信息http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044e/index.html。如果您不关心展开,则不需要它们(展开对于 C++ 异常和调试很有用)。

  2. 这些符号与 C/C++ 构造函数和析构函数的启动和拆除在 main() 之前/之后调用的代码有关。名为 .init、.ctors、.preinit_array 和 .init_array 的部分用于初始化 C/C++ 对象,而 .fini、.fini_array 和 .dtors 部分用于拆卸。开始和结束符号定义与此类操作相关的代码段的开始和结束,并且可能从运行时支持代码的其他部分引用。

.preinit_array 和 .init_array 部分包含指向将在初始化时调用的函数的指针数组。.fini_array 是一个函数数组,将在销毁时调用。大概开始和结束标签用于遍历这些列表。

  1. 堆:不是真的,那部分允许为堆保留一些空间,为堆栈保留一些空间。显然,如果保留区域的总和超出 RAM 边界,则会出现错误。这是一个例子:

    _Min_Heap_Size = 0; /* 所需的堆数量 / _Min_Stack_Size = 0x400; /所需的堆栈数量 */

    ._user_heap_stack : { . =对齐(4);提供(结束=。);提供(_end = .);. = 。+ _Min_Heap_Size; . = 。+ _Min_Stack_Size; . =对齐(4);} >内存

  2. 链接库我更喜欢不同的符号,这只是裸机无 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)

于 2016-11-11T00:16:53.723 回答
-1

首先,您理解这个概念的方法是错误的;这就是我所相信的。在理解这个概念的过程中,我也遇到过类似的问题。

用简单的语言,我可以向您解释链接描述文件为我们提供了三个主要内容:

  1. 入口点
  2. 主内存中的运行时地址。
  3. 抄录程序

让我们考虑一个例子,

假设我们的项目中有 N 个 .c 文件。现在编译后每个文件都包含自己的翻译单元,称为目标文件。

每个目标文件都包含.text包含实际代码的节/段。和 .data 部分/数据段。

为了组合.text每个翻译单元的所有部分,链接描述文件提供了一些特定的命令。这.data部分也是一样的。

组合所有目标文件后,最终的可执行文件就可以使用了。

现在来到一些悖论......

Cortex-M 系列的入口点就是 ResetISR。在执行完 ResetISR 函数并初始化 SoC 中的其他可屏蔽中断后,下一步就是向下复制过程。

复制过程只不过是将.data部分复制到 RAM 中(甚至包括有趣的.bss部分,但我现在不考虑该部分)。

从 ROM 复制到 RAM 是必不可少的,因为实际的 ELF 文件始终存储在 ROM 中。

在执行了所有这些与启动相关的事情之后,我们现在可以调用我们的main()函数了。

于 2016-11-11T05:08:21.177 回答