STM32 GCC编译器.ld文件详解

.ld文件的作用

1.定义程序入口地址
2.定义Flash、RAM中代码和数据的存放位置

/* Entry Point */
/* 程序入口——程序将从Reset Handler开始执行,而该函数定义在stm32fxxx.s启动文件中。
ENTRY(Reset_Handler)

/* Highest address of the user mode stack /
/
end of stack 堆栈末尾 = RAM起始地址 + RAM空间大小 /
_estack = ORIGIN(RAM) + LENGTH(RAM); /
end of “RAM” Ram type memory */

/* 程序所必须的堆、栈空间大小定义 /
_Min_Heap_Size = 0x200 ; /
required amount of heap /
_Min_Stack_Size = 0x400 ; /
required amount of stack */

/* Memories definition /
/
单片机内部存储空间 RAM FLASH起始位置和大小的声明 */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 512K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K
}

/* Sections /
SECTIONS
{
/
The startup code into “FLASH” Rom type memory /
/
中断向量表定义于 .s启动文件中,应位于Flash的最前端,也就是从0x8000000的位置开始 /
.isr_vector :
{
/
字对齐 /
. = ALIGN(4);
/
将中断向量的内容链接到该地址 /
KEEP(
(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH

/* The program code and other data into “FLASH” Rom type memory /
/
.text对应程序的可执行代码 /
.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)

/* KEEP() 的作用是当启用连接器的--gc-sections垃圾回收选项时,这部分不能被回收  
参考链接 https://blog.csdn.net/wangbinyantai/article/details/79001303 */
KEEP (*(.init))
KEEP (*(.fini))

. = ALIGN(4);
/* _etext是链接器的预定义变量,代表程序正文段结束的第一个地址 */
_etext = .;        /* define a global symbols at end of code */

} >FLASH

/* Constant data into “FLASH” Rom type memory /
/
.rodata代表程序中使用的常量表格数据等 /
.rodata :
{
/
前后字对齐 /
. = ALIGN(4);
(.rodata) / .rodata sections (constants, strings, etc.) /
(.rodata) /
.rodata
sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH

/* ?如果有朋友清楚下面这段定义的含义请评论告诉我,多谢!/
.ARM.extab : {
. = ALIGN(4);
(.ARM.extab .gnu.linkonce.armextab.
)
. = ALIGN(4);
} >FLASH

.ARM : {
. = ALIGN(4);
__exidx_start = .;
(.ARM.exidx)
__exidx_end = .;
. = ALIGN(4);
} >FLASH

/* .preinit_array和.init_array部分包含指向将在初始化时调用的函数的指针数组。
参考链接:https://stackoverflow.com/questions/40532180/understanding-the-linkerscript-for-an-arm-cortex-m-microcontroller */

.preinit_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP ((.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
} >FLASH

.init_array :
{
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP ((SORT(.init_array.)))
KEEP ((.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
} >FLASH

.fini_array :
{
. = ALIGN(4);

/* PROVIDE_HIDDEN 表示 :Similar to PROVIDE. For ELF targeted ports, 
the symbol will be hidden and won’t be exported. 
表示符号在目标文件中被定义但不会被导出 */

PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);

} >FLASH

/* Used by the startup to initialize data /
/
以变量_sidata存储.data块的起始地址,.data对应初始化了的全局变量 */

_sidata = LOADADDR(.data);

/* Initialized data sections into “RAM” Ram type memory /
/
.data对应初始化了的全局变量,编译后将位于可执行文件中,由启动代码负责加载到数据区中(在单片机中这部分数据会存于flash中,需要由启动代码把这部分内容拷贝到ram中)/
.data :
{
. = ALIGN(4);
_sdata = .; /
create a global symbol at data start /
(.data) / .data sections /
(.data) /
.data
sections */

. = ALIGN(4);

/* edata的地址是初始化数据区后面的第1个地址 */
_edata = .;        /* define a global symbol at data end */

} >RAM AT> FLASH

/* Uninitialized data section into “RAM” Ram type memory */
. = ALIGN(4);

/* .bss段是没有初始值的全局变量,由启动代码把这部分内容全初始化为0 /
.bss :
{
/
This is used by the startup in order to initialize the .bss section /
_sbss = .; /
define a global symbol at bss start */
bss_start = _sbss;
(.bss)
(.bss)
/
COMMON数据段仅在.o文件中存在,当代码中的全局变量没有赋初值时存储在该数据段中 */
https://stackoverflow.com/questions/16835716/bss-vs-common-what-goes-where */

*(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” Ram type memory left */
._user_heap_stack :
{
. = ALIGN(8);

/* 同PROVIDE_HIDDEN的作用类似,定义内部变量而不导出 */
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);

} >RAM

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

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

.s启动文件

gcc工具链和armcc的.s启动文件内容差别很大,我感觉都不像ARM汇编…

  .syntax unified /* 意思是下面的指令是ARM和THUMB通用格式的 */
  .cpu cortex-m7 /* CPU内核型号 */
  .fpu softvfp /* 选择使用的浮点单元,与-mfpu命令行选项的相同 */
  .thumb /* 选择使用thumb指令 */

/* 声明可以被汇编器使用的符号 /
.global g_pfnVectors /
在文件末尾定义的中断向量 /
.global Default_Handler /
是一个死循环,用来处理异常情况 */

/* start address for the initialization values of the .data section.
defined in linker script /
.word _sidata /
初始值地址 /
/
start address for the .data section. defined in linker script /
.word _sdata /
.data 开始地址,.data对应初始化了的全局变量,编译后将位于可执行文件中,由启动代码负责加载到数据区中(在单片机中这部分数据会存于flash中,需要由启动代码把这部分内容拷贝到ram中) /
/
end address for the .data section. defined in linker script /
.word _edata /
.data块的结束地址 /
/
start address for the .bss section. defined in linker script /
.word _sbss /
.bss块的起始地址,.bss段是没有初始值的全局变量,由启动代码把这 部分内容全初始化为0 /
/
end address for the .bss section. defined in linker script /
.word _ebss /
.bss块的结束地址 /
/
stack used for SystemInit_ExtMemCtl; always internal RAM used */

/**

  • @brief This is the code that gets called when the processor first
  •      starts execution following a reset event. Only the absolutely
    
  •      necessary set is performed, after which the application
    
  •      supplied main() routine is called. 
    
  • @param None
  • @retval : None
    */
  • /* 定义Reset_Handler函数,该函数的作用1.设置堆栈指针;2.全局变量的初始化 /
    .section .text.Reset_Handler
    .weak Reset_Handler
    .type Reset_Handler, %function
    Reset_Handler:
    ldr sp, =_estack /
    set stack pointer */

    /* Copy the data segment initializers from flash to SRAM /
    movs r1, #0 /
    将r1初始化为0 /
    b LoopCopyDataInit
    /
    下面一段初始化在用户程序中指定初始值的全局变量 /
    CopyDataInit:
    ldr r3, =_sidata /
    使用ldr伪指令将初始数据地址加载到r3中 /
    ldr r3, [r3, r1] /
    寄存器间接寻址,将r3 + r1地址中的数据加载到r3中 /
    str r3, [r0, r1] /
    将r3的内容写入 r0 + r1地址中 /
    adds r1, r1, #4 /
    后变址,将r1地址中的内容写入r1,然后令r1 + 4 */

    LoopCopyDataInit:
    ldr r0, =_sdata /* 使用ldr伪指令,在r0中写入.data的起始地址 /
    ldr r3, =_edata /
    在r3中写入.data的末尾地址 /
    adds r2, r0, r1 /
    r2 = r0 + r1 ,操作影响条件标志位 /
    cmp r2, r3 /
    比较r2和r3寄存器中的地址大小 /
    bcc CopyDataInit /
    如果r2 < r3,也就是还没有到达data数据段的末尾,则转跳CopyDataInit /
    /
    因为汇编语言顺序执行,上面代码会循环执行,直到复制到.data数据段结束 /
    ldr r2, =_sbss /
    r2中存储.bss数据区的首地址*/
    b LoopFillZerobss

    /* Zero fill the bss segment. /
    FillZerobss:
    movs r3, #0 /
    将寄存器r3写0 /
    str r3, [r2], #4 /
    采用后变址,先将r3中的值写入r2,也就是bss数据区首地址中,然后r2自加4 */

    LoopFillZerobss:
    ldr r3, = _ebss /* 将bss数据区的末尾地址写入r3 /
    cmp r2, r3 /
    比较r2和r3的内容,看是否已经到达数据区的末尾 /
    bcc FillZerobss /
    如果r2 < r3也就是没有到达末尾,则转跳FillZerobss继续写0操作 */

    /* Call the clock system initialization function./
    // bl SystemInit /
    转跳SystemInit函数 /
    /
    Call static constructors /
    bl __libc_init_array
    /
    Call the application’s entry point./
    bl main /
    转跳main函数执行 */
    bx lr
    .size Reset_Handler, .-Reset_Handler

    /**

  • @brief This is the code that gets called when the processor receives an
  •     unexpected interrupt.  This simply enters an infinite loop, preserving
    
  •     the system state for examination by a debugger.
    
  • @param None
  • @retval None
    /
    .section .text.Default_Handler,“ax”,%progbits
    Default_Handler: /
    默认异常处理代码段,是一段死循环 /
    Infinite_Loop:
    b Infinite_Loop
    .size Default_Handler, .-Default_Handler
    /
    *****************************************************************************
  • The minimal vector table for a Cortex M7. Note that the proper constructs
  • must be placed on this to ensure that it ends up at physical address
  • 0x0000.0000.
  • *******************************************************************************/
    .section .isr_vector,“a”,%progbits
    .type g_pfnVectors, %object
    .size g_pfnVectors, .-g_pfnVectors

    g_pfnVectors:
    .word _estack
    .word Reset_Handler

    .word NMI_Handler
    .word HardFault_Handler
    .word MemManage_Handler
    .word BusFault_Handler
    .word UsageFault_Handler
    .word 0
    .word 0
    .word 0
    .word 0
    .word SVC_Handler
    .word DebugMon_Handler
    .word 0
    .word PendSV_Handler
    .word SysTick_Handler

    /* External Interrupts /
    .word WWDG_IRQHandler /
    Window WatchDog /
    .word PVD_IRQHandler /
    PVD through EXTI Line detection /
    .word TAMP_STAMP_IRQHandler /
    Tamper and TimeStamps through the EXTI line /
    .word RTC_WKUP_IRQHandler /
    RTC Wakeup through the EXTI line /
    .word FLASH_IRQHandler /
    FLASH /
    .word RCC_IRQHandler /
    RCC /
    .word EXTI0_IRQHandler /
    EXTI Line0 /
    .word EXTI1_IRQHandler /
    EXTI Line1 /
    .word EXTI2_IRQHandler /
    EXTI Line2 /
    .word EXTI3_IRQHandler /
    EXTI Line3 /
    .word EXTI4_IRQHandler /
    EXTI Line4 /
    .word DMA1_Stream0_IRQHandler /
    DMA1 Stream 0 */
    /节省篇幅,其他类似定义略/

    /*******************************************************************************
    *

  • Provide weak aliases for each Exception handler to the Default_Handler.
  • As they are weak aliases, any function with the same name will override
  • this definition.
  • *******************************************************************************/
    .weak NMI_Handler
    .thumb_set NMI_Handler,Default_Handler

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 GCC编译器.ld文件详解

    发表回复