GD32的Makefile+启动文件+LD链接文件配置
看到一篇很好的文章附一下:
https://www.cnblogs.com/RegressionWorldLine/p/14819836.html
Makefile
对于Makefile没啥好说的,本质是继承预处理编译和汇编,一些不懂得都写在了注释里
需要更改的地方有:
TARGET
C_SOURCES
ASM_SOURCES
CPU FPU FLOAT-ABI
C_INCLUDES
LDSCRIPT
加密:@$(ENCRYPT_TOOL) $@ ./lppc_boot.xml
(
B
U
I
L
D
D
I
R
)
/
(BUILD_DIR)/
(BUILDDIR)/(TARGET)_encrypt.bin
# lppc_boot
TARGET = lppc_boot
# 判断当前系统是windows,mac还是linux
# DEBUG release
ifdef RELEASE
DEBUG = 0
else
DEBUG = 1
endif
# 优化等级
OPT = -O1
# 创建在build目录下
BUILD_DIR = build
# 包含文件
C_SOURCES = \
${wildcard CMSIS/*.c} \
${wildcard USER/*.c} \
${wildcard protocol/*.c} \
${wildcard protocol/support/*.c} \
${wildcard Platform/*.c} \
#${wildcard applications/*.c}
# ASM sources 启动文件
ASM_SOURCES = STARTUP/startup_gd32fl23x.s
# 编译工具链
PREFIX = arm-none-eabi-
# 工具链路径,已经加入环境变量则不需要定义GCC_PATH
ifdef GCC_PATH
CC = $(GCC_PATH)/$(PREFIX)gcc
AS = $(GCC_PATH)/$(PREFIX)gcc -x assembler-with-cpp
CP = $(GCC_PATH)/$(PREFIX)objcopy
SZ = $(GCC_PATH)/$(PREFIX)size
else
CC = $(PREFIX)gcc
AS = $(PREFIX)gcc -x assembler-with-cpp
CP = $(PREFIX)objcopy
SZ = $(PREFIX)size
endif
# 编译hex和bin文件
HEX = $(CP) -O ihex
BIN = $(CP) -O binary -S
CPU = -mcpu=cortex-m23
#无浮点运算(硬件浮点)
#浮点运算处理方式
FPU =
FLOAT-ABI =
MCU = $(CPU) -mthumb $(FPU) $(FLOAT-ABI)
AS_DEFS =
C_DEFS =
ifdef RELEASE
C_DEFS += -DRELEASE
endif
AS_INCLUDES =
# C includes -I代表在这个参数指定的目录下去寻找
C_INCLUDES = \
-ICMSIS \
-IUSER \
-Iprotocol \
-Iprotocol/support \
-IPlatform \
#-Iapplications
# compile gcc flags
ASFLAGS = $(MCU) $(AS_DEFS) $(AS_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
CFLAGS += $(MCU) $(C_DEFS) $(C_INCLUDES) $(OPT) -Wall -fdata-sections -ffunction-sections
ifeq ($(DEBUG), 1)
CFLAGS += -g -gdwarf-2
endif
# -M 生成文件关联的信息。包含目标文件所依赖的所有源代码
# -MMD 生成文件关联的信息。包含目标文件所依赖的所有源代码,但是它将忽略由#include造成的依赖关系,输出将导入到.d的文件里面
# -MF 指一个文件用于存放生成文件的关联信息,这些信息与-M或-MM是一样的,所以要与-M或-MM一起使用,否则会报错,
#将.o文件变为.d文件
CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"
# 链接ld地址
LDSCRIPT = FLASH_LINK/GD32FL233KBQx_FLASH.ld
# libraries
#map文件包含信息分为一下5大类
#1.Section Cross References:模块、段(入口)交叉引用
#2.Removing Unused input sections from the image:移除未调用模块
#3.Image Symbol Table:映射符号表
#4.Memory Map of the image:内存(映射)分布
#5.Image component sizes:存储组成大小
LIBS = -lc -lm -lnosys
LIBDIR =
LDFLAGS = $(MCU) -specs=nano.specs -T$(LDSCRIPT) $(LIBDIR) $(LIBS) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections
# addprefix 将前缀加到name前面,将所有.c文件转换成.0,notdir去掉路径,sort去重复路径,dir取路径,与notdir相反
# vpath <pattern> <directories> 设置符合pattern的文件的搜索路径是directories
# % 匹配任何字符 $@ 表示目标文件 $^ 表示所有的依赖文件 $< 表示第一个依赖文件 $? 表示比目标还要新的依赖文件列表
OBJECTS = $(addprefix $(BUILD_DIR)/,$(notdir $(C_SOURCES:.c=.o)))
vpath %.c $(sort $(dir $(C_SOURCES)))
# list of ASM program objects
OBJECTS += $(addprefix $(BUILD_DIR)/,$(notdir $(ASM_SOURCES:.s=.o)))
vpath %.s $(sort $(dir $(ASM_SOURCES)))
#最终要生成elf,hex,bin文件
all: $(BUILD_DIR)/$(TARGET).elf $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin
# | $(BUILD_DIR)标志是生成build这个目录,若以生成则忽略,将.s和.c转换成.o
$(BUILD_DIR)/%.o: %.c | $(BUILD_DIR)
@echo compiling $@
@$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
$(BUILD_DIR)/%.o: %.s | $(BUILD_DIR)
@echo assembling $@
@$(AS) -c $(CFLAGS) $< -o $@
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS)
@echo linking
@$(CC) $(OBJECTS) $(LDFLAGS) -o $@
@$(SZ) $@
$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf
@echo creating hex file $@
@$(HEX) $< $@
#生产bin文件,同时生产加密文件bin
$(BUILD_DIR)/%.bin: $(BUILD_DIR)/%.elf
@echo creating bin file $@
@$(BIN) $< $@
# @$(ENCRYPT_TOOL) $@ ./lppc_boot.xml $(BUILD_DIR)/$(TARGET)_encrypt.bin
$(BUILD_DIR):
mkdir $@
clean:
@echo delete $(BUILD_DIR)
@-rm -fR $(BUILD_DIR)
-include $(wildcard $(BUILD_DIR)/*.d)
# *** EOF ***
启动文件
需要更改cotex-m23,中断向量表,若之后要加入boot自升级,需要多拷贝一次。
一些基本指令不理解的也有注释。
//.syntax [unified | divided] 。 作用是在汇编ARM汇编源时,指定按照怎样的语法规则进行汇编。
.syntax unified
.cpu cortex-m23
.thumb
//.global 告诉编译器后续跟的是一个全局度可见的名字
.global g_pfnVectors
.global Default_Handler
// .word 表示与处理器有关的字长
/* 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
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
//.section指示把代码划分成若干个段(Section),
//程序被操作系统加载执行时,每个段被加载到不同的地址
.section .text.Reset_Handler //这里Reset_Handler被分在了.text段
.weak Reset_Handler // 弱符号,如果其他地方未定义相同的强符号,则此处的符号将会被链接
.type Reset_Handler, %function //设置符号的属性,function意为函数
Reset_Handler:
ldr sp, =_estack /* set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
ldr r0, =_sdata ;从存储器中将_sdata的地址加载到寄存器r0
ldr r1, =_edata ;从存储器中将_edata的地址加载到寄存器r1
ldr r2, =_sidata ;从存储器中将_sidata的地址加载到寄存器r2
movs r3, #0 ;赋予r3立即数0
b LoopCopyDataInit ;程序转移到LoopCopyDataInit处
CopyDataInit:;从FLASH中拷贝地址在sdata和edata之间的代码到SRAM中
ldr r4, [r2, r3] ;从地址r2+r3处读取一个字(32bit)到r4中
str r4, [r0, r3] ;把寄存器r4的值存储到存储器中地址为r0+r3地址处
adds r3, r3, #4 ;r3 = r3 + 4
LoopCopyDataInit:
adds r4, r0, r3 ;r4=r0+r3
cmp r4, r1 ;计算r4 - r1,若小于0,标志位为0,反之为1
bcc CopyDataInit ;如果标志位为0(无借位)即r4<r1 CopyDataInit处
ldr r0, =_sbootram ;从存储器中将_sbootram的地址加载到寄存器r0
ldr r1, =_ebootram ;从存储器中将_ebootram的地址加载到寄存器r1
ldr r2, =_sibootram ;从存储器中将_sibootram的地址加载到寄存器r2
movs r3, #0
b LoopFillZerobss ;程序转移到LoopCopyDataInit2处
FillZerobss:
str r3, [r2] ;把寄存器r3的值存储到存储器中地址为r2地址处
adds r2, r2, #4 ;r2 = r2 + 4
LoopFillZerobss:
cmp r2, r4 ;计算r2 - r4,若小于0,标志位为0,反之为1
bcc FillZerobss ;如果标志位为0(无借位)即r2<r4 跳到FillZerobss
/* Call the clock system initialization function.*/
//BL指令 将当前 指令 的地址存储到链接寄存器中,
//以便在执行完跳转 指令 后,能够返回到跳转前的地址
bl SystemInit ;初始化时钟
/* Call static constructors */
bl __libc_init_array;
/* Call the application's entry point.*/
bl main
//LR寄存器一是用来保存子程序返回地址;二是当异常发生时,
//LR中保存的值等于异常发生时PC的值减4(或者减2),
//因此在各种异常模式下可以根据LR的值返回到异常发生前的相应位置继续执行。
bx lr ;bx lr的作用等同于mov pc,lr即跳转到lr中存放的地址处。
.size Reset_Handler, .-Reset_Handler ;.size算符,汇编程序将回送分配给该变量的字节数
/**
* @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 M3. 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 ;.isr_vector就是中断向量表段
.type g_pfnVectors, %object ;%object表示符号为数据对象.
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler ;复位中断
.word NMI_Handler ;不可屏蔽中断
.word HardFault_Handler ;硬件中断,内存溢出或者访问越界,堆栈溢出。增加堆栈的大小
.word 0 ;访问了内存管理单元(MPU)定义的不合法的内存区域,比如向只读区域写入数据
.word 0 ;在fetch指令、数据读写、fetch中断向量或中断时存储恢复寄存器栈情况下,检测到内存访问错误则产生BusFault。
.word 0 ;检测到未定义指令或在存取内存时有未对齐。还可以通过软件配置是否检测到除数为0和其它未对齐内存访问也产生该异常,默认关闭,需要在工程初始化时配置。
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word 0
.word 0
.word PendSV_Handler
.word SysTick_Handler
/* external interrupts handler */
.word WWDGT_IRQHandler /* Window Watchdog interrupt */
.word LVD_IRQHandler /* PVD through EXTI line */
.word TAMPER_STAMP_IRQHandler /* RTC tamper, timestamp */
.word RTC_WKUP_IRQHandler /* RTC Wakeup interrupt */
.word FMC_IRQHandler /* Flash memory */
.word RCU_CTC_IRQHandler /* RCC global interrupt */
.word EXTI0_IRQHandler /* EXTI Line 0 interrupt */
.word EXTI1_IRQHandler /* EXTI Line 1 interrupt */
.word EXTI2_IRQHandler /* EXTI Line 2 interrupt */
.word EXTI3_IRQHandler /* EXTI Line 3interrupt */
.word EXTI4_IRQHandler /* EXTI Line 4interrupt */
.word DMA_Channel0_IRQHandler /* DMA1 Stream0 */
.word DMA_Channel1_IRQHandler /* DMA1 Stream1 */
.word DMA_Channel2_IRQHandler /* DMA1 Stream2 */
.word DMA_Channel3_IRQHandler /* DMA1 Stream3 */
.word DMA_Channel4_IRQHandler /* DMA1 Stream4 */
.word DMA_Channel5_IRQHandler /* DMA1 Stream5 */
.word DMA_Channel6_IRQHandler /* DMA1 Stream6 */
.word ADC_IRQHandler /* ADC1 and ADC2 */
.word USBD_HP_IRQHandler /* FDCAN1 Interrupt 0 */
.word USBD_LP_IRQHandler /* FDCAN2 Interrupt 0 */
.word TIMER1_IRQHandler /* FDCAN1 Interrupt 1 */
.word TIMER2_IRQHandler /* FDCAN2 Interrupt 1 */
.word TIMER8_IRQHandler /* EXTI Line[9:5] interrupts */
.word TIMER11_IRQHandler /* TIM1 break interrupt */
.word TIMER5_IRQHandler /* TIM1 update interrupt */
.word TIMER6_IRQHandler /* TIM1 trigger and commutation */
.word USART0_IRQHandler /* TIM1 capture / compare */
.word USART1_IRQHandler /* TIM2 global interrupt */
.word UART3_IRQHandler /* TIM3 global interrupt */
.word UART4_IRQHandler /* TIM4 global interrupt */
.word I2C0_EV_IRQHandler /* I2C1 event interrupt */
.word I2C0_ER_IRQHandler /* I2C1 global error interrupt */
.word I2C1_EV_IRQHandler /* I2C2 event interrupt */
.word I2C1_ER_IRQHandler /* I2C2 global error interrupt */
.word SPI0_IRQHandler /* SPI1 global interrupt */
.word SPI1_IRQHandler /* SPI2 global interrupt */
.word DAC_IRQHandler /* USART1 global interrupt */
.word 0 /* USART2 global interrupt */
.word I2C2_EV_IRQHandler /* USART3 global interrupt */
.word I2C2_ER_IRQHandler /* EXTI Line[15:10] interrupts */
.word RTC_Alarm_IRQHandler /* RTC alarms (A and B) */
.word USBD_WKUP_IRQHandler /* Reserved */
.word EXTI5_9_IRQHandler /* TIM8 and 12 break global */
.word 0 /* TIM8 and 13 update global */
.word 0 /* TIM8 and 14 trigger /commutation and global */
.word 0 /* TIM8 capture / compare */
.word EXTI10_15_IRQHandler /* DMA1 Stream7 */
.word 0 /* FMC global interrupt */
.word 0 /* SDMMC1 global interrupt */
.word 0 /* TIM5 global interrupt */
.word 0 /* SPI3 global interrupt */
.word 0 /* UART4 global interrupt */
.word 0 /* UART5 global interrupt */
.word 0 /* TIM6 global interrupt */
.word DMAMUX_IRQHandler /* TIM7 global interrupt */
.word CMP0_IRQHandler /* DMA2 Stream0 interrupt */
.word CMP1_IRQHandler /* DMA2 Stream1 interrupt */
.word I2C0_WKUP_IRQHandler /* DMA2 Stream2 interrupt */
.word I2C2_WKUP_IRQHandler /* DMA2 Stream3 interrupt */
.word USART0_WKUP_IRQHandler /* DMA2 Stream4 interrupt */
.word LPUART_IRQHandler /* Ethernet global interrupt */
.word CAU_IRQHandler /* Ethernet wakeup through EXTI */
.word TRNG_IRQHandler /* CAN2TX interrupts */
.word SLCD_IRQHandler /* Reserved */
.word USART1_WKUP_IRQHandler /* Reserved */
.word I2C1_WKUP_IRQHandler /* Reserved */
.word LPUART_WKUP_IRQHandler /* Reserved */
.word LPTIMER_IRQHandler /* DMA2 Stream5 interrupt */
/*******************************************************************************
*
* 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
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDGT_IRQHandler
.thumb_set WWDGT_IRQHandler,Default_Handler
.weak LVD_IRQHandler
.thumb_set LVD_IRQHandler,Default_Handler
.weak TAMPER_STAMP_IRQHandler
.thumb_set TAMPER_STAMP_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FMC_IRQHandler
.thumb_set FMC_IRQHandler,Default_Handler
.weak RCU_CTC_IRQHandler
.thumb_set RCU_CTC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_IRQHandler
.thumb_set EXTI2_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA_Channel0_IRQHandler
.thumb_set DMA_Channel0_IRQHandler,Default_Handler
.weak DMA_Channel1_IRQHandler
.thumb_set DMA_Channel1_IRQHandler,Default_Handler
.weak DMA_Channel2_IRQHandler
.thumb_set DMA_Channel2_IRQHandler,Default_Handler
.weak DMA_Channel3_IRQHandler
.thumb_set DMA_Channel3_IRQHandler,Default_Handler
.weak DMA_Channel4_IRQHandler
.thumb_set DMA_Channel4_IRQHandler,Default_Handler
.weak DMA_Channel5_IRQHandler
.thumb_set DMA_Channel5_IRQHandler,Default_Handler
.weak DMA_Channel6_IRQHandler
.thumb_set DMA_Channel6_IRQHandler,Default_Handler
.weak ADC_IRQHandler
.thumb_set ADC_IRQHandler,Default_Handler
.weak USBD_HP_IRQHandler
.thumb_set USBD_HP_IRQHandler,Default_Handler
.weak USBD_LP_IRQHandler
.thumb_set USBD_LP_IRQHandler,Default_Handler
.weak TIMER1_IRQHandler
.thumb_set TIMER1_IRQHandler,Default_Handler
.weak TIMER2_IRQHandler
.thumb_set TIMER2_IRQHandler,Default_Handler
.weak TIMER8_IRQHandler
.thumb_set TIMER8_IRQHandler,Default_Handler
.weak TIMER11_IRQHandler
.thumb_set TIMER11_IRQHandler,Default_Handler
.weak TIMER5_IRQHandler
.thumb_set TIMER5_IRQHandler,Default_Handler
.weak TIMER6_IRQHandler
.thumb_set TIMER6_IRQHandler,Default_Handler
.weak USART0_IRQHandler
.thumb_set USART0_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak UART3_IRQHandler
.thumb_set UART3_IRQHandler,Default_Handler
.weak UART4_IRQHandler
.thumb_set UART4_IRQHandler,Default_Handler
.weak I2C0_EV_IRQHandler
.thumb_set I2C0_EV_IRQHandler,Default_Handler
.weak I2C0_ER_IRQHandler
.thumb_set I2C0_ER_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak SPI0_IRQHandler
.thumb_set SPI0_IRQHandler,Default_Handler
.weak SPI1_IRQHandler
.thumb_set SPI1_IRQHandler,Default_Handler
.weak DAC_IRQHandler
.thumb_set DAC_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak USBD_WKUP_IRQHandler
.thumb_set USBD_WKUP_IRQHandler,Default_Handler
.weak EXTI5_9_IRQHandler
.thumb_set EXTI5_9_IRQHandler,Default_Handler
.weak EXTI10_15_IRQHandler
.thumb_set EXTI10_15_IRQHandler,Default_Handler
.weak DMAMUX_IRQHandler
.thumb_set DMAMUX_IRQHandler,Default_Handler
.weak CMP0_IRQHandler
.thumb_set CMP0_IRQHandler,Default_Handler
.weak CMP1_IRQHandler
.thumb_set CMP1_IRQHandler,Default_Handler
.weak I2C0_WKUP_IRQHandler
.thumb_set I2C0_WKUP_IRQHandler,Default_Handler
.weak I2C2_WKUP_IRQHandler
.thumb_set I2C2_WKUP_IRQHandler,Default_Handler
.weak USART0_WKUP_IRQHandler
.thumb_set USART0_WKUP_IRQHandler,Default_Handler
.weak LPUART_IRQHandler
.thumb_set LPUART_IRQHandler,Default_Handler
.weak CAU_IRQHandler
.thumb_set CAU_IRQHandler,Default_Handler
.weak TRNG_IRQHandler
.thumb_set TRNG_IRQHandler,Default_Handler
.weak SLCD_IRQHandler
.thumb_set SLCD_IRQHandler,Default_Handler
.weak USART1_WKUP_IRQHandler
.thumb_set USART1_WKUP_IRQHandler,Default_Handler
.weak I2C1_WKUP_IRQHandler
.thumb_set I2C1_WKUP_IRQHandler,Default_Handler
.weak LPUART_WKUP_IRQHandler
.thumb_set LPUART_WKUP_IRQHandler,Default_Handler
.weak LPTIMER_IRQHandler
.thumb_set LPTIMER_IRQHandler,Default_Handler
.weak SystemInit
/************************ (C) COPYRIGHT STMicroelectonics *****END OF FILE****/
链接文件
入口地址:ENTRY(SYMBOL),这里SYMBOL位Reset_Handler
/* Entry Point */
ENTRY(Reset_Handler)
定义内存
MEMORY 命令
脚本中以MEMORY命令定义了存储空间,其中以ORIGIN定义地址空间的起始地址,LENGTH定义地址空间的长度.NAME :存储区域的名字。(自己可以随意命名)
ATTR :定义该存储区域的属性。ATTR属性内可以出现以下7 个字符:
R 只读section
W 读/写section
X 可执行section
A 可分配的section
I 初始化了的section
L 同 I
! 不满足该字符之后的任何一个属性的section
ORIGIN :关键字,区域的开始地址,可简写成 org 或 o
LENGTH :关键字,区域的大小,可简写成 len 或 l
GD内存分配
- 设置RAM结束地址:起始地址为0x20000000,ram大小为24K,结束地址为0x20006000.
- 设置堆栈:由于使用的heap4,不使用标准库的malloc因此这里配置0x0,
系统栈配置为0x600,1536字节,够用
了。
/* Highest address of the user mode stack */
_estack = 0x20006000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x0; /* required amount of heap */
_Min_Stack_Size = 0x600; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 24K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K
}
malloc的优缺点
为何使用heap4而不适用malloc进行内存分配,主要是通过heap4分配它有自带的首次适应算法和内存合并算法,同时它还能够反馈给我们更多的信息用于我们进行调试,在ozone上还能够看到内存块进行分析。
malloc的优缺点如下:
优点:
- 动态分配内存:malloc函数可以根据需要动态分配内存空间,可以根据程序的实际需求来分配合适大小的内存,提高程序的灵活性和效率。
- 可以根据需求释放内存:分配的内存可以通过free函数进行释放,可以在不需要该内存时及时释放,避免内存的浪费。
- 分配的内存可以在函数之间传递:通过malloc分配的内存可以在函数之间传递,可以在函数调用结束后,仍然保留分配的内存空间。
缺点: - 需要手动管理内存:使用malloc函数分配的内存需要手动释放,否则会造成内存泄漏,导致程序运行效率低下或者内存耗尽。
- 容易出现内存分配错误:如果使用不当,比如分配的内存大小不够或者释放的内存不正确,会导致程序出现错误,如内存读写错误、段错误等。
- 对内存的使用需要精确控制:使用malloc分配的内存需要精确控制大小和使用方式,否则容易导致内存溢出或者内存访问错误。
配置section段和符号
section和关键字
SECTIONS
{
...
secname [start_ADDR] [(TYPE)] : [AT (LMA_ADDR)]
{
contents
} [>REGION] [AT>LMA_REGION] [:PHDR HDR ...] [=FILLEXP]
...
}
[ ]内的内容是可选选项.
secname: 表示输出文件的 section 名,即输出文件中有哪些 section。
而contents就是描述输出文件的这个 section 内容从哪些输入文件的哪些 section 里抽取而来。
输出section名字必须符合输出文件格式要求,比如:a.out格式的文件只允许存在.text、.data和.bss section名。而有的格式只允许存在数字名字,那么此时应该用引号将所有名字内的数字组合在一起;另外,还有一些格式允许任何序列的字符存在于 section名字内,此时如果名字内包含特殊字符(比如空格、逗号等),那么需要用引号将其组合在一起.
TYPE:每个输出section都有一个类型,如果没有指定TYPE类型,那么链接器根据输出section引用的输入section的类型设置该输出section的类型。它可以为以下五种值
- NOLOAD 该section在程序运行时,不被载入内存。
- DSECT,COPY,INFO,OVERLAY :这些类型很少被使用,为了向后兼容才被保留下来。这种类型的section必须被标记为“不可加载的”,以便在程序运行不为它们分配内存。
AT( LAM_ADDR ):输出 section 的 LMA,默认情况下 LMA 等于 VMA,但可以通过关键字 AT() 指定 LMA。用关键字 AT()
指定,括号内包含表达式,表达式的值用于设置LMA。如果不用AT()
关键字,那么可用AT>LMA_REGION
表达式设置指定该section加载地址的范围。这个属性主要用于构建ROM镜像。
[>REGION]:这个region就是前面说的MEMORY命令定义的位置信息,用于指定section在哪个memory执行,也就是VMA。如果不指定LMA,LMA = VMA。
KEEP 关键字
在链接命令行内使用了选项 -gc-sections 后,链接器可能将某些它认为没用的 section 过滤掉,此时就有必要强制让链接器保留一些特定的 section,可用 KEEP() 关键字达此目的。如 KEEP(* (.text)) 或 KEEP(SORT(*)(.text))。说的通俗易懂就是:防止被优化。
ALIGN 关键字
表示字节对齐, 如 “ . = ALIGN(4);”表示从该地址开始后面的存储进行4字节对齐。
LOADADDR()
进行地址设定
HIDDEN
通过赋值语句定义的符号是全局的,如果想定义一个只在本链接脚本中可见的符号,可以通过HIDDEN(symbol = expression)命令来定义,例如:
HIDDEN(start_of_data = 0x123456)
该命令类似C语言中在函数外定义一个static变量
PROVIDE_HIDDEN
该命令同PROVIDE
,区别是PROVIDE
定义的符号是全局,PROVIDE_HIDDEN
定义的符号只在本脚本中可见
VMA 和 LMA
section包含两个地址:VMA
(virtual memory address虚拟内存地址)和LMA
(load memory address加载内存地址)。通常VMA和LMA是相同的。
- VMA是执行输出文件时section所在的地址
- LMA是加载输出文件时section所在的地址
要作为运行地址,首先PC指针要能在这个地址空间内跑动,所以这段地址空间必须是可随机寻址的,也就是说可以访问想要访问的地址,如RAM,NorFlash(Norflash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地存取其内部的每一个字节);如果是NandFlash,则不行,因为NandFlash只能通过一个块一个块的读/写,不能做到随机寻址。
VMA和LMA大多数情况下是相等的,但也可以不相等。通常,当LMA地址空间不支持随机寻址,或者是嫌弃LMA地址空间的访问速度比较慢时(比如NorFlash速度比SDRAM慢),则会将VMA设置到RAM中,这时,VMA与LMA就不相等了。
例:NandFlash因为不能随机访问想要访问的每个地址,不能作为运行地址,所以这里想要把在NandFlash的代码复制到SDRAM中。这里除了代码中要加入复制模块外,还要在链接脚本中使NandFlash部分的VMA设为SDRAM地址。大多数情况下,是在起始代码中初始化时就将需要复制的部分复制的到VMA地址空间中。
还有一种情况就是当嵌入式系统中先都将代码和数据加载到了ROM中,此时的地址就是LMA,但是当开始运行之后,需要将data数据部分拷贝到RAM中,此时数据的地址就是VMA,本文最开始的链接脚本,就是把.data的LMA设置在ROM,VMA设置在RAM。
GD配置section段和符号
中断向量表段,代码段 的配置
例子:*(.text)描述语句指示ld将所有输入文件的.text
段输出到输出文件的.text
段,*
匹配所有输入文件
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH//最终在flash中去执行
/* The program code and other data goes into FLASH */
.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 (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .; // 设置变量
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array段,.init_array 段,.fini_array 段
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
配置_sidata的地址给启动文件调用
_sidata = LOADADDR(.data);
配置数据段和BSS段以及 ._user_heap_stack :
RAM AT> FLASH表示在flash中加载,在ram中运行
/* Initialized data sections goes into RAM, load LMA copy after code */
.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 AT> FLASH
/* Uninitialized data section */
. = 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
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
清除
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }