单片机下gcc安全堆栈模型的实现

一、安全堆栈模型示意图

从上图很容易看出:

  • 该模型采用了“背向生长”的方式——避免了栈与堆的相互伤害

  • 栈被放在了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%开发者从未听说过的堆栈模型(加量增强版)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值