STM32裸机开发(9) — 使用链接脚本链接代码
一、链接脚本的作用
链接脚本的作用就是用来指定程序的链接方式的,一个程序中包含各种文件,例如start.o、main.o、led.o等,每个文件有包含如代码段、数据段等各种段,而链接脚本的作用就是用来指定各种文件各种段的链接方式。前面我们都没有使用链接文件,只使用了-Ttext
参数来指明代码段的链接地址,其他都是按照默认链接的,使用之前曾强调要将start.o
文件放在最前面。
二、编写链接文件
将Makefile中的链接命令改为如下所示
$(LD) -g $(OBJECTS) -T stm32f103zet6.ld -o $@
接着编写一个最简单的链接文件stm32f103zet6.ld
如下所示,其实这个和之前的-Ttext 0x80100000
参数效果是一样的。首先SECTIONS {}
是链接文件的语法,表示程序的所有段都在其中;然后. = 0x80100000
表示当前地址设置为0x80100000,亦即链接的起始地址为0x80100000
;. = ALIGN(4)
表示当前地址按4字节对齐;.text
表示段名,*(.text)
表示将所有文件的代码段都存放在此。
SECTIONS {
. = 0X8000000;
. = ALIGN(4);
.text :
{
*(.text)
}
}
我们打开反汇编文件,可以看到段名为.text
的段,同时其链接地址也是从0X8000000开始的
三、指定内存区域
因为对于在STM32F103这类资源紧缺的单片机芯片中:
- 代码段保存在Flash上,直接在Flash上运行(当然也可以重定位到内存里)
- 数据段暂时先保存在Flash上,然后在使用前被复制到内存里(只读数据段不复制)
所以我们要给予代码段和数据段不同的保存地址,在链接文件中添加如下内存区域指定说明,分别定义RAM和FLASH两个内存区域,分别对应stm32芯片上的ram和flash区域,其中Flash区域可读可执行
,开始地址为0x08000000
,长度为512K,Ram区域可读可写可执行
,开始地址为0x20000000
,长度为 64K
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
}
然后添加只读数据段、数据段、BSS段并指定其内存区域,修改完的链接文件如下
/* 指定内存区域 */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS {
.text :
{
. = ALIGN(4);
*start.o (.text)
*main.o (.text)
*(.text)
} > FLASH
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
}
然后修改main程序添加如下全局变量
使用make编译,打开反汇编文件,找到如下所示,可以看到这三个段分别存放着刚刚定义的C语言中的全局变量。
首先,只读数据段.rodata
中放置加const
修饰的变量myconst,其存放在Flash区域;
然后,数据段data
中已初始化的全局变量mydata,其存放在Ram区域;
最后,bss
段放置未初始化或初始化为零的全局变量,同样也存放在Ram区域。
四、验证
修改mian.c如下所示,添加各个变量地址的打印
#include "uart.h"
#include "led.h"
int mydata = 0x12315;
const int myconst = 0x22315;
int myzero = 0;
int my;
int main(void)
{
uart_init();
led_init();
putstring("stm32f103zet6\r\n");
putstring("mydata\t:");
puthex((unsigned int)&mydata);
putstring("\r\nmyconst\t:");
puthex((unsigned int)&myconst);
putstring("\r\nmyzero\t:");
puthex((unsigned int)&myzero);
putstring("\r\nmy\t:");
puthex((unsigned int)&my);
putstring("\r\n");
while(1)
{
putstring("led on\r\n");
led_on();
delay(1000000);
putstring("led off\r\n");
led_off();
delay(1000000);
}
}
编译烧录运行,并于反汇编文件做对比,可以看到,其地址完全符合:
五、附录
上一篇:STM32裸机开发(8) — 在gcc环境下编写uart串口打印程序
下一篇:STM32裸机开发(10) — 复制data段和清除BSS段