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

这篇具有很好参考价值的文章主要介绍了STM32 GCC编译器 .ld & .s文件详细解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

.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

.ld文件的作用

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

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

到了这里,关于STM32 GCC编译器 .ld & .s文件详细解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux:编译器 - gcc

    GCC(英文全拼:GNU Compiler Collection)是 GNU 工具链的主要组成部分,是一套以 GPL 和 LGPL 许可证发布的程序语言编译器自由软件,由 Richard Stallman 于 1985 年开始开发。 gcc是GCC中的C语言编译器,而g++是GCC中的C++编译器。本博客只讲解gcc,g++的语法和选项和gcc都是一致的。 gcc 编译

    2024年04月14日
    浏览(65)
  • MinGW-w64的安装详细步骤(c/c++的编译器gcc、g++的windows版,win10、win11真实可用)

    MinGW(Minimalist GNU for Windows) 是一个用于 Windows 平台的开发工具集,它提供了一组 GNU 工具和库,可以用于编译和构建本地的 Windows 应用程序。 MinGW 的目标是在 Windows 环境下提供类似于 Unix/Linux 环境下的开发工具,使开发者能够轻松地在 Windows 上编写和编译 C、C++ 等程序。

    2024年02月13日
    浏览(50)
  • Linux GCC常用命令以及GCC编译器

    GCC 是编译工具,它的意思是 GNU C Compiler 。经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言;它现在还支持 Ada 语言、C++ 语言、Java 语言、Objective C 语言、Pascal 语言、COBOL语言,以及支持函数式编程和逻辑编程的 Mercury 语言等等。而 GCC 也不再单只是 GNU C 语言编译器的意

    2024年02月05日
    浏览(35)
  • Ubuntu中安装gcc编译器

    文章目录 前言 一、gcc编译器是什么? 二、使用步骤 1.Ubuntu安装gcc编译器 2.使用gcc编译器生成可自行文件 在Ubuntu中是可以直接编译C语言文件的,需要安装gcc编译器,在Ubuntu中默认是没有安装gcc编译器需要自己手动安装,安装完直接就可以使用 build-essential就是gcc的安装包,提

    2024年02月08日
    浏览(28)
  • Linux——gcc/g++编译器

    目录 I.Linux编译器 1.gcc/g++编译器 在C代码生成可执行程序的过程中,会有四个过程: 1预处理,2编译,3汇编,4链接 Linux对.c文件分辨进行预处理,编译,汇编三大步指令: 预处理指令: 编译指令: 汇编指令: 接下来说一说链接过程: II.动静态链接  一.动态链接 二.静态链接

    2024年02月04日
    浏览(36)
  • Linux编译器gcc/g++

    以gcc编译 以g++编译,但是此时会发现没有g++这个指令,所有需要安装它,安装指令 yum install gcc gcc-c++ gcc和g++都会形成可执行文件a.out gcc只能编译c语言代码,g++能编译c/c++ 以c程序为例,来看看它从一个文本类的c程序编译成计算机可以认识的二进制程序它需要经过四个阶段 预

    2024年02月10日
    浏览(33)
  • 解决keil5.38编译stm32报四个错误问题,无需更换ARM5编译器

     以上为错误截图,一下为错误信息 Start/core_cm3.c(445): error: non-ASM statement in naked function is not supported   uint32_t result=0;   ^ Start/core_cm3.c(442): note: attribute is here uint32_t __get_PSP(void) __attribute__( ( naked ) );                                           ^ Start/core_cm3.c(465): error: paramet

    2024年02月11日
    浏览(68)
  • 【Linux】03 GCC编译器的使用

     在使用gcc编译程序时,编译过程可以简要划分为4个阶段:         预处理、编译、汇编、链接 这个阶段主要处理源文件中的#indef、#include和#define预处理命令; 这里主要是把一些include的头文件和一些宏定义,放到源文件中。 编译命令: gcc  -E  -o  hello.i  hello.c 将经过预处

    2024年01月20日
    浏览(37)
  • Linux编译器 gcc与g++

    程序的编译过程: 1、 预处理 (头文件包含、消除注释、宏定义替换) 2、 编译 (将语言替换成汇编代码) 3、 汇编 (将汇编指令转换为二进制指令) 4、 链接 (合并段表、符号表合并及重定位) 我们可以通过gcc工具实现程序的编译过程: 2.1 预处理 预处理会完成:①头

    2023年04月18日
    浏览(61)
  • Linux编译器——gcc/g++使用

    前言:  在上一篇,我们学习了关于文本编辑器 vim 的全部知识,今天给大家带来的是关于Linux编译器—gcc/使用的详细介绍。 本文目录  (一)温习程序的产生的过程 1、前言 2、程序的产生过程 3、🌜初步认识 gcc🌛 a) gcc的基本概念 b)gcc的基本特点 4、使用方法💻 (二)

    2023年04月17日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包