| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 嵌入式 -> 【windows下基于Eclipse搭建stm32开发环境(3)】GCC链接脚本(.ld)文件详解 -> 正文阅读 |
|
[嵌入式]【windows下基于Eclipse搭建stm32开发环境(3)】GCC链接脚本(.ld)文件详解 |
GCC链接脚本.ld 文件详解关于eclipse搭建stm32开发环境的内容请移步:【windows下基于Eclipse搭建stm32开发环境】 关于eclipse + stm32 + hex和bin文件下载请移步:【windows下基于Eclipse搭建stm32开发环境(2)】hex和bin文件下载到单片机 ~~ 原创文章,转载请注明出处 ! 一、链接脚本的配置:当我们用eclipse插件创建一个stm32工程的时候,会发现其中有三个.ld文件 其中mem.ld和sections.ld就是对应我们stm32的链接脚本文件,libs.ld是一个空文件,后续扩展使用,可以不管 二、mem.ld文件讲解打开mem.ld文件: 1、MEMORY命令:是GCC链接器支持语法中的内存块配置命令,一个连接脚本最多一个’MEMORY’命令。 MEMORY语法如下:
NAME是内存区域的名字,可任意编写,尽在链接脚本中有实际意义。
A、 如果一个未映射节匹配了上面除’!'之外的一个属性,它就会被放入该内存区域。 '!'属性对该测试取反,所以只有当它不匹配上面列出的行何属性时,一个未映射节才会被放入到内存区域。
LEN
在下面的例子中,我们指定两个可用于分配的内存区域:
了解了MEMORY命令的用法后,mem.ld文件的代码也很容易看懂了,就是定义了两个存储区,一个只读的Flash,和一个可读写的RAM 三、sections.ld文件讲解:接下来打开sections.ld文件 这里有一个PROVIDE命令,如下图所示:
分别表示栈的最大最小值,和堆的起始地址 1、PROVIDE关键字语法:想象某些情况下, 一个符号被引用到的时候只在连接脚本中定义,而不在任何一个被连接进来的目标文件中定义.这种做法比较明智.比如, 传统的连接器定义了一个符号’etext’. ANSI C 需要用户能够把’etext’作为一个函数使用,这时不会产生错误. 语法是 :
下面是一个关于使用’PROVIDE’定义’etext’的例子:
在这个例子中, 如果程序定义了一个’_etext’(带有一个前导下划线), 连接器会给出一个重定义错误. 如果,程序定义了一个’etext’(不带前导下划线), 连接器会默认使用程序中的定义. 如果程序引用了’etext’但不定义它,连接器会使用连接脚本中的定义 总结下来就是: 也就是说_Main_Stack_Size,_Main_Stack_Limit,_Heap_Begin,_Heap_Limit这四个变量我们可以在C程序中重定义 2、ENTRY命令:接下来是一个ENTRY命令,如下图所示 运行一个程序时第一个被执行到的指令称为"入口点",默认是start,可以使用’ENTRY’连接脚本命令来设置入口点.参数是一个符号名:
因为STM32的.text段的头部是向量表,0地址存放是的栈顶地址,不是程序入口,所以需要重新来指定入口地址,其实对于STM32来说入口地址只要是写在复位中断中就可以,链接脚本中的声明主要是为了调试和仿真 3、SECTIONS命令:接下来就是【段】命令了,这也是链接脚本中最重要的部分 SECTIONS命令告诉连接器如何把输入节映射到输出节, 并如何把输入节放入到内存中.isr_vector节。 4、ALTGN命令:5、FILL命令:用‘FILL’命令来为当前节设置填充样式。它后面跟有一个括号中的表达式。任何未指定的节内内存区域(比如,因为输入节的对齐要求而造成的裂缝)会以这个表达式的值进行填充。 6、ABSOLUTE命令:7、KEEP()命令:如下图所示,keep命令,主要作用是防止垃圾收集机制把这两个重要的节排除在外,另外呢也保证了向量表在段中的位置处于最顶端 那么(.isr_vector)是啥意思呢?* 首先搜索一下,看看工程中哪里定义的:
找到.isr_vector定义的地方,猛的一看,好像看不懂,这里出现一个__attribute__ 8、补充一下__attribute__机制的知识:attribute 机制是GUN C的一大特色,主要作用是设置函数属性、变量属性、和类型属性
attribute 语法格式为:attribute ((attribute-list))
attribute 的常参数介绍:
指定对象的对齐格式(以字节为单位),如:
该声明将强制编译器确保(尽它所能)变量类 型为struct S 或者int32_t 的变量在分配空间时采用8字节对齐方式。
使用该属性对struct 或者union 类型进行定义,设定其类型的每一个变量的内存约束。就是告诉编译器取消结构在编译过程中的优化对齐(使用1字节对齐),按照实际占用字节数进行对齐,是GCC特有的语法。 下面的例子中,packed_struct 类型的变量数组中的值将会紧紧的靠在一起,但内部的成员变量s 不会被“pack” ,如果希望内部的成员变量也被packed 的话,unpacked-struct 也需要使用packed 进行相应的约束。
在GCC下:struct my{ char ch; int a; sizeof(int)=4;sizeof(my)=8(非紧凑模式)
绝对定位,可以把变量或函数绝对定位到Flash中,或者定位到RAM。 1)、定位到flash中,一般用于固化的信息,如出厂设置的参数,上位机配置的参数,ID卡的ID号,flash标记等等
2)、定位到RAM中,一般用于数据量比较大的缓存,如串口的接收缓存,再就是某个位置的特定变量 注意: 1)、绝对定位不能在函数中定义,局部变量是定义在栈区的,栈区由MDK自动分配、释放,不能定义为绝对地址,只能放在函数外定义。 2)、定义的长度不能超过栈或Flash的大小,否则,造成栈、Flash溢出。
提到section,就得说RO RI ZI了,在ARM编译器编译之后,代码被划分为不同的段,RO Section(ReadOnly)中存放代码段和常量,RW Section(ReadWrite)中存放可读写静态变量和全局变量,ZI Section(ZeroInit)是存放在RW段中初始化为0的变量。 于是本文的大体意思就清晰了,attribute((section(“section_name”))),其作用是将作用的函数或数据放入指定名为"section_name"对应的段中。 1)、编译时为变量指定段:
2)、编译时为函数指定段
第二个例子:
表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
向编译器说明这段代码有用,即使在没有用到的情况下编译器也不会警告!
该属性通知编译器函数从不返回值。当遇到类似函数还未运行到return语句就需要退出来的情况,该属性可以避免出现错误信息。
weak 参数是__attribute__ 机制中的若符号声明,如果有两个同名的函数,而其中一个被声明为弱符号,则不会触发重定义错误,编译时自动跳过若符号声明的代码段
这是一个STM32的内部可悬挂异常中断函数,在移植OS时,我们需要重定义该函数,而刚开始建立工程时,就用weak声明若定义 好了,复习过__attribute__机制的用法后,再STM32向量表: 再回过头来开sections.ld链接脚本: KEEP(*(.isr_vector))的意思就是,在GCC编译汇编之后的链接过程中,把所有名为.isr_vector的段的代码,放到.isr_vector节的最前面,也就是把向量表放在生成的二进制文件的最前面。相信玩儿keil MDK软件或学过STM32单片机的人应该了解这一点。 .isr_vector节的最后一部分*(.after_vectors .after_vectors.*) ,分析方法是一样的,其实就是把stm32内部异常的处理函数放在名为.after_vectors的段,然后链接时放在.isr_vector节的最后面,不再过多赘述 搜索一下.after_vectors段的定义的地方,可以看到,该段都包含了那些代码: 它的意思是这个段放在名为FLASH的内存块,由于我们mem.ld脚本中已经定义好了内存块儿地址,所以该.isr_vector的编码地址就是FLASH,起始为0x8000000 .inits节首先是两个小段落,如下图所示:
这一句的语法很简单,就是定义一个符号,等于当前地址 LOADADDR(.data): 获取.data段的加载地址(lma),也就是data段在Flash中存放的起始地址 那么还有一个问题,为什么会有.data段和.bss段?它们是哪儿来的? 请看下面这张图,GCC在编译C语言文件的时候,会分别生成RO、RW、ZI部分,RO是只读段,也就是程序代码段(.text),就是具体函数代码,RW是读写数据段(.data),也就是初始化的全局变量,ZI为未初始化数据段(.bss),也就是那些未赋初值的变量,这个段不占用内存空间,只有在程序运行的时候,在RAM初始化为0;链接脚本的作用也就是将这些编译出来的段整合到一起 好了,知道上面的语法后,这部分代码的意思就很清晰了: inits后半段解析如下图所示: 该节没有什么用,可以直接跳过 下面.text节:
那么单片机怎么知道.data存放在Flash的什么地址呢?看下图,_sidata记录了.data对应的Flash地址 .bss节: 结语** 更多内容见后续文章 ~~~~**。 |
|
嵌入式 最新文章 |
基于高精度单片机开发红外测温仪方案 |
89C51单片机与DAC0832 |
基于51单片机宠物自动投料喂食器控制系统仿 |
《痞子衡嵌入式半月刊》 第 68 期 |
多思计组实验实验七 简单模型机实验 |
CSC7720 |
启明智显分享| ESP32学习笔记参考--PWM(脉冲 |
STM32初探 |
STM32 总结 |
【STM32】CubeMX例程四---定时器中断(附工 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 11:59:30- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |