STM32 GCC编译器 .ld & .s文件详细解析

.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

  • 1
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值