一、安全堆栈模型示意图
从上图很容易看出:
-
该模型采用了“背向生长”的方式——避免了栈与堆的相互伤害
-
栈被放在了SRAM的起始位置(Cortex-M从架构上鼓励将SRAM放置在从0x2000-0000开始的地址上),这样一旦发生栈溢出,指针就会指向SRAM存储器以外的无效位置——这在大部分芯片上会触发“Bus Fault”,从而产生故障异常——这就实现了对栈溢出的当场捕获,并且不依赖MPU或者“栈底地址限制检测(Stack Limit Checking)”之类的架构特性。
当然有些芯片设计者可能会选择“隐藏这类错误”,不仅不会触发异常,而且会当做无事发生,具体表现为:对无效地址的写入操作将被无视,对无效地址的读取操作将会返回0值。具体可以参考芯片手册,或者干脆做个实验。
-
堆被放置在了RAM的最后,中间夹着存放静态/全局变量的“RW/ZI区域(bss, data)”,这也是“两面包夹芝士”模型(或者“三明治”模型)名称的由来。这样的安排也彻底杜绝了栈和堆对“RW/ZI区域”发生入侵的可能。当堆溢出时,与栈类似,对大部分芯片来说都会触发故障异常,从而在开发调试阶段第一时间被我们所捕获
-
通过链接脚本的一些运算功能,我们甚至可以做到“将剩下的空间全留给HEAP”,从而简化系统的配置。
二、链接脚本的配置
STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : 0x800;
ENTRY(Reset_Handler)
MEMORY
{
/* flash 256k 权限:可读可执行 */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256k
/* 内存 48k 权限:可读可写可执行 */
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 48K
}
SECTIONS
{
/* 中断向量表 */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.vectors)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* 代码段 */
.text :
{
. = ALIGN(4);
_stext = .;
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
} >FLASH
/* 栈空间分配由大地址(栈顶)-->小地址(0x20000000),如果溢出则会触发hardfault */
.stack (NOLOAD) :
{
/* Cortex-M架构下栈存储空间必须对齐到8字节 */
_sstack = ORIGIN(RAM);
. = . + STACK_SIZE ;
. = ALIGN(8);
_estack = .;
} >RAM
/* 数据段,在flash加载,在ram运行,需要在启动文件中将数据从flash拷贝至ram */
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} >RAM AT>FLASH
/* LOADADDR(section) 返回名为section的段的LMA绝对地址 */
/* 获取.data段在flash的绝对地址,在启动文件中拷贝数据时用到 */
_rdata = LOADADDR(.data);
/* 未初始化的数据段,需要在启动文件中进行清零 */
/* NOLOAD:该section在程序运行时,不被载入内存;
这里是告诉链接器bss段不占用image空间 */
.bss (NOLOAD) :
{
. = ALIGN(8);
_sbss = .;
*(.bss*)
*(COMMON)
. = ALIGN(8);
_ebss = .;
} >RAM
/* 堆空间分配由小地址-->大地址(0x2000c000),如果溢出则会触发hardfault */
.heap (NOLOAD) :
{
. = ALIGN(8);
_sheap = .;
. = ORIGIN(RAM) + LENGTH(RAM) - .;
_eheap = .;
} >RAM
}
查看map分析
三、启动文件的配置(startup.c)
#include <stdint.h>
#include "system_gd32f30x.h"
// 这些参数需要在链接脚本中定义了才能引用
extern uint32_t _sdata, _edata, _rdata, _sbss, _ebss, _estack;
void main(void);
void Reset_Handler(void)
{
volatile uint32_t *src, *dst;
// 将数据从flash拷贝至ram
// copy data in [_sdata, _edata] to _rdata
src = &_rdata;
dst = &_sdata;
while (dst < &_edata)
*dst++ = *src++;
// 将bss段清零,即未初始化的静态/全局变量清零
// set data in [_sbss, _ebss] to zero
dst = &_sbss;
while (dst < &_ebss)
*dst++ = 0;
SystemInit();
main();
while (1)
;
}
void Null_Handler(void) { return; }
void Loop_Handler(void)
{
while (1)
;
}
//Dummy interrupt handlers
void __attribute__((weak, alias("Null_Handler"))) NMI_Handler(void);
void __attribute__((weak, alias("Loop_Handler"))) HardFault_Handler(void);
void __attribute__((weak, alias("Loop_Handler"))) MemManage_Handler(void);
void __attribute__((weak, alias("Loop_Handler"))) BusFault_Handler(void);
void __attribute__((weak, alias("Loop_Handler"))) UsageFault_Handler(void);
void __attribute__((weak, alias("Null_Handler"))) SVC_Handler(void);
void __attribute__((weak, alias("Null_Handler"))) DebugMon_Handler(void);
void __attribute__((weak, alias("Null_Handler"))) PendSV_Handler(void);
void __attribute__((weak, alias("Null_Handler"))) SysTick_Handler(void);
void __attribute__((weak, alias("Null_Handler"))) WWDGT_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) LVD_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TAMPER_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) RTC_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) FMC_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) RCU_CTC_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) EXTI0_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) EXTI1_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) EXTI2_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) EXTI3_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) EXTI4_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA0_Channel0_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA0_Channel1_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA0_Channel2_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA0_Channel3_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA0_Channel4_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA0_Channel5_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA0_Channel6_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) ADC0_1_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) USBD_HP_CAN0_TX_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) USBD_LP_CAN0_RX0_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) CAN0_RX1_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) CAN0_EWMC_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) EXTI5_9_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER0_BRK_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER0_UP_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER0_TRG_CMT_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER0_Channel_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER1_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER2_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER3_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) I2C0_EV_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) I2C0_ER_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) I2C1_EV_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) I2C1_ER_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) SPI0_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) SPI1_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) USART0_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) USART1_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) USART2_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) EXTI10_15_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) RTC_Alarm_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) USBD_WKUP_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER7_BRK_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER7_UP_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER7_TRG_CMT_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER7_Channel_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) ADC2_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) EXMC_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) SDIO_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER4_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) SPI2_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) UART3_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) UART4_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER5_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) TIMER6_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA1_Channel0_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA1_Channel1_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA1_Channel2_IRQHandler(void);
void __attribute__((weak, alias("Null_Handler"))) DMA1_Channel3_4_IRQHandler(void);
//Interrupt vector table
const __attribute__((section(".vectors"))) uint32_t _VECTORS[] =
{
(uint32_t)&_estack, // Top of Stack
(uint32_t)Reset_Handler, // Reset Handler
(uint32_t)NMI_Handler, // NMI Handler
(uint32_t)HardFault_Handler, // Hard Fault Handler
(uint32_t)MemManage_Handler, // MPU Fault Handler
(uint32_t)BusFault_Handler, // Bus Fault Handler
(uint32_t)UsageFault_Handler, // Usage Fault Handler
0, 0, 0, 0, // reserved x 4
(uint32_t)SVC_Handler, // SVCall Handler
(uint32_t)DebugMon_Handler, // Debug Monitor Handler
0, // reserved
(uint32_t)PendSV_Handler, // PendSV Handler
(uint32_t)SysTick_Handler, // SysTick Handler
/* external interrupts handler */
(uint32_t)WWDGT_IRQHandler, // 16:Window Watchdog Timer
(uint32_t)LVD_IRQHandler, // 17:LVD through EXTI Line detect
(uint32_t)TAMPER_IRQHandler, // 18:Tamper through EXTI Line detect
(uint32_t)RTC_IRQHandler, // 19:RTC through EXTI Line
(uint32_t)FMC_IRQHandler, // 20:FMC
(uint32_t)RCU_CTC_IRQHandler, // 21:RCU and CTC
(uint32_t)EXTI0_IRQHandler, // 22:EXTI Line 0
(uint32_t)EXTI1_IRQHandler, // 23:EXTI Line 1
(uint32_t)EXTI2_IRQHandler, // 24:EXTI Line 2
(uint32_t)EXTI3_IRQHandler, // 25:EXTI Line 3
(uint32_t)EXTI4_IRQHandler, // 26:EXTI Line 4
(uint32_t)DMA0_Channel0_IRQHandler, // 27:DMA0 Channel0
(uint32_t)DMA0_Channel1_IRQHandler, // 28:DMA0 Channel1
(uint32_t)DMA0_Channel2_IRQHandler, // 29:DMA0 Channel2
(uint32_t)DMA0_Channel3_IRQHandler, // 30:DMA0 Channel3
(uint32_t)DMA0_Channel4_IRQHandler, // 31:DMA0 Channel4
(uint32_t)DMA0_Channel5_IRQHandler, // 32:DMA0 Channel5
(uint32_t)DMA0_Channel6_IRQHandler, // 33:DMA0 Channel6
(uint32_t)ADC0_1_IRQHandler, // 34:ADC0 and ADC1
(uint32_t)USBD_HP_CAN0_TX_IRQHandler, // 35:USBD HP and CAN0 TX
(uint32_t)USBD_LP_CAN0_RX0_IRQHandler, // 36:USBD LP and CAN0 RX0
(uint32_t)CAN0_RX1_IRQHandler, // 37:CAN0 RX1
(uint32_t)CAN0_EWMC_IRQHandler, // 38:CAN0 EWMC
(uint32_t)EXTI5_9_IRQHandler, // 39:EXTI5 to EXTI9
(uint32_t)TIMER0_BRK_IRQHandler, // 40:TIMER0 Break
(uint32_t)TIMER0_UP_IRQHandler, // 41:TIMER0 Update
(uint32_t)TIMER0_TRG_CMT_IRQHandler, // 42:TIMER0 Trigger and Commutation
(uint32_t)TIMER0_Channel_IRQHandler, // 43:TIMER0 Channel Capture Compare
(uint32_t)TIMER1_IRQHandler, // 44:TIMER1
(uint32_t)TIMER2_IRQHandler, // 45:TIMER2
(uint32_t)TIMER3_IRQHandler, // 46:TIMER3
(uint32_t)I2C0_EV_IRQHandler, // 47:I2C0 Event
(uint32_t)I2C0_ER_IRQHandler, // 48:I2C0 Error
(uint32_t)I2C1_EV_IRQHandler, // 49:I2C1 Event
(uint32_t)I2C1_ER_IRQHandler, // 50:I2C1 Error
(uint32_t)SPI0_IRQHandler, // 51:SPI0
(uint32_t)SPI1_IRQHandler, // 52:SPI1
(uint32_t)USART0_IRQHandler, // 53:USART0
(uint32_t)USART1_IRQHandler, // 54:USART1
(uint32_t)USART2_IRQHandler, // 55:USART2
(uint32_t)EXTI10_15_IRQHandler, // 56:EXTI10 to EXTI15
(uint32_t)RTC_Alarm_IRQHandler, // 57:RTC Alarm
(uint32_t)USBD_WKUP_IRQHandler, // 58:USBD Wakeup
(uint32_t)TIMER7_BRK_IRQHandler, // 59:TIMER7 Break
(uint32_t)TIMER7_UP_IRQHandler, // 60:TIMER7 Update
(uint32_t)TIMER7_TRG_CMT_IRQHandler, // 61:TIMER7 Trigger and Commutation
(uint32_t)TIMER7_Channel_IRQHandler, // 62:TIMER7 Channel Capture Compare
(uint32_t)ADC2_IRQHandler, // 63:ADC2
(uint32_t)EXMC_IRQHandler, // 64:EXMC
(uint32_t)SDIO_IRQHandler, // 65:SDIO
(uint32_t)TIMER4_IRQHandler, // 66:TIMER4
(uint32_t)SPI2_IRQHandler, // 67:SPI2
(uint32_t)UART3_IRQHandler, // 68:UART3
(uint32_t)UART4_IRQHandler, // 69:UART4
(uint32_t)TIMER5_IRQHandler, // 70:TIMER5
(uint32_t)TIMER6_IRQHandler, // 71:TIMER6
(uint32_t)DMA1_Channel0_IRQHandler, // 72:DMA1 Channel0
(uint32_t)DMA1_Channel1_IRQHandler, // 73:DMA1 Channel1
(uint32_t)DMA1_Channel2_IRQHandler, // 74:DMA1 Channel2
(uint32_t)DMA1_Channel3_4_IRQHandler, // 75:DMA1 Channel3 and Channel4
};
四、系统调用底层接口的实现(syscall.c)
/* Includes */
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#undef errno
extern int errno;
extern int _eheap;
extern int _sheap;
/* Variables */
//#undef errno
extern int errno;
extern int __io_putchar(int ch) __attribute__((weak));
extern int __io_getchar(void) __attribute__((weak));
register char *stack_ptr asm("sp");
char *__env[1] = {0};
char **environ = __env;
// /* Functions */
// void initialise_monitor_handles()
// {
// }
int _getpid(void)
{
return 1;
}
int _kill(int pid, int sig)
{
errno = EINVAL;
return -1;
}
void _exit(int status)
{
_kill(status, -1);
while (1)
{
} /* Make sure we hang here */
}
//__attribute__((weak)) int _read(int file, char *ptr, int len)
int _read(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
*ptr++ = __io_getchar();
}
return len;
}
// __attribute__((weak)) int _write(int file, char *ptr, int len)
int _write(int file, char *ptr, int len)
{
int DataIdx;
for (DataIdx = 0; DataIdx < len; DataIdx++)
{
__io_putchar(*ptr++);
}
return len;
}
int _close(int file)
{
return -1;
}
int _fstat(int file, struct stat *st)
{
st->st_mode = S_IFCHR;
return 0;
}
int _isatty(int file)
{
return 1;
}
int _lseek(int file, int ptr, int dir)
{
return 0;
}
// int _open(char *path, int flags, ...)
// {
// /* Pretend like we always fail */
// return -1;
// }
// int _wait(int *status)
// {
// errno = ECHILD;
// return -1;
// }
// int _unlink(char *name)
// {
// errno = ENOENT;
// return -1;
// }
// int _times(struct tms *buf)
// {
// return -1;
// }
// int _stat(char *file, struct stat *st)
// {
// st->st_mode = S_IFCHR;
// return 0;
// }
// int _link(char *old, char *new)
// {
// errno = EMLINK;
// return -1;
// }
// int _fork(void)
// {
// errno = EAGAIN;
// return -1;
// }
// int _execve(char *name, char **argv, char **env)
// {
// errno = ENOMEM;
// return -1;
// }
// 这个是malloc的底层实现,从堆(heap)中分配空间
// _sheap在ld链接脚本中定义
int _sbrk(int incr)
{
static unsigned char *heap = NULL;
unsigned char *prev_heap;
if (heap == NULL)
{
heap = (unsigned char *)&_sheap;
}
prev_heap = heap;
heap += incr;
return (int)prev_heap;
}
附:mdk实现参考
参考微信公众号裸机思维的文章:这么好用?!99%开发者从未听说过的堆栈模型(加量增强版)