我使用的是来自STM32F746NG的STMicroelectronics微控制器。该设备基于ARM Cortex-M7体系结构。我花了相当多的时间来理解示例项目中的linkerscript。我弄明白了基本知识,但我仍然不能掌握其中的大部分。请帮助我理解这些部分。
链接脚本的启动
链接脚本的开头如下:
/* 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
}矢量表和程序代码
在定义了内存区域之后,链接脚本就会继续定义这些部分。在linkerscript中定义的第一部分是向量表。它必须在闪存的第一个字节中结束。
/* --------------------------------------------------------------------*/
/* OUTPUT SECTIONS */
/* --------------------------------------------------------------------*/
SECTIONS
{
/****************************/
/* VECTOR TABLE */
/****************************/
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Vector Table */
. = ALIGN(4);
} >FLASH插入向量表后,程序代码的时间到了:
/****************************/
/* 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链接脚本定义了e_text全局符号,该符号表示闪存中的程序代码结束的地址。
常数数据
只读数据也会出现在闪存中(将其放入不稳定的RAM中是没有意义的)。链接脚本定义了.rodata部分应该是闪存的:
/****************************/
/* CONSTANT DATA */
/****************************/
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH神秘的瞬间
在定义了常量只读数据的去向之后,linkerscript定义了一些“神秘”部分也应该在闪存中结束:
.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我不知道这些部分是什么。所以,让这是第一个问题。这些部分是什么,它们在哪些对象文件中显示?如您所知,链接脚本需要将一些对象文件链接到一起。我不知道这些神秘的部分存在于哪些对象文件中:
.ARM.extab.ARM.preinit_array.init_array.fini_array这是对闪存分配的结束。链接脚本继续定义在RAM中结束的部分。
RAM中的部分
.data和.bss部分对我来说是清楚的。这件事没问题。
/****************************/
/* 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链接脚本还定义了一个._user_heap_stack部分:
/****************************/
/* 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显然这部分没有立即使用。它仅用于检查RAM是否仍然有足够的空间容纳堆栈和堆。当情况不是这样( .超过最高内存地址)时,将引发链接器错误。
链接脚本的结尾
链接脚本就是这样结束的。老实说,我不知道它是干什么的。这是第二个问题:以下是什么意思?
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}
/* END OF LINKERSCRIPT */发布于 2016-11-11 00:16:53
.preinit_array和.init_array部分包含指向初始化时将调用的函数的指针数组。.fini_array是一个函数数组,将在销毁时被调用。据推测,开始和结束标签用于遍历这些列表。
发布于 2016-11-11 05:08:21
首先,你理解这个概念的方法是错误的,这就是我所相信的。在理解这个概念的过程中,我也遇到了类似的问题。
用简单的语言,我可以解释链接器脚本为我们提供了三个主要的东西:
所以让我们考虑一个例子,
假设我们的项目中有N个.c文件。现在,编译后,每个文件包含自己的翻译单元,称为对象文件。
每个对象文件都包含包含实际代码的.text节/段。以及数据的.data部分/段。
为了组合每个翻译单元的所有.text部分,链接器脚本提供了一些特定的命令。这对于.data部分也是一样的。
合并所有对象文件之后,最终的可执行文件就可以使用了。
现在讲到一些悖论..。
在Cortex-M系列的情况下,入口点就是ResetISR.在执行ResetISR函数并在SoC中初始化其他可掩蔽中断之后,下一步是复制过程。
复制过程只不过是将.data部分复制到内存中(其中甚至包括有趣的.bss部分,但我现在不考虑这个部分)。
从ROM复制到RAM是必要的,因为实际的ELF文件总是存储在ROM中。
在执行了所有这些与启动相关的事情之后,我们现在可以调用我们的main()函数了。
https://stackoverflow.com/questions/40532180
复制相似问题