.ld文件的作用
1.定义程序入口地址
2.定义Flash、RAM中代码和数据的存放位置文章来源地址https://www.toymoban.com/news/detail-610196.html
/* 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文章来源:https://www.toymoban.com/news/detail-610196.html
.ld文件的作用
1.定义程序入口地址
2.定义Flash、RAM中代码和数据的存放位置
/* Entry Point */
/* 程序入口——程序将从Reset Handler开始执行,而该函数定义在stm32fxxx.s启动文件中。
ENTRY(Reset_Handler)
到了这里,关于STM32 GCC编译器 .ld & .s文件详细解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!