简介:
在查看IAR 的使用手册中,看到 --stack_protection 编译选项,从该编译选项的的描述可以大致知道,开启了该选项后编译器会在函数的入口添加堆栈保存__stack_chk_guard 变量的值,函数退出时对__stack_chk_guard 栈空间数据进行check,如果和预期的值不一致则认为发生栈的overflow 事件,并调用void __stack_chk_fail(void) 函数,告诉用户发生了stack 的越界事件,从而实现对函数栈的保护功能。
使用IAR 环境验证:
根据上面的说明,我们开启对应的编译选项没对应路径如下:
我们在IAR 环境中打开对应的配置开关,本地使用的IAR 版本为9.50.2
开启后重编译的选项中也可以看到,编译器追加了 --stack_protection 编译选项
同时在链接的时候也会提示,如下的符号找不到,对应的符号很熟悉就是我们上面说到需要我们提供的两个符号。
这两个符号在IAR 的安装目录下已经提供了模板文件,我们直接使用即可,对应路径如下,我们将该文加添加检出异常打印加入到工程后使用即可,就会解决上述链接符号找不到的问题。
对应代码如下:
/******************* * * Copyright 2017 IAR Systems AB. * * This is a template implementation of support needed for * stack smash protection. * * Stack smash protection is implemented by placing a canary variable * between the return address and the rest of the functions stack frame. * ********************/ #include <stdlib.h> /* For void abort(void) */ #include <stdint.h> /* For uint32_t */ #include "fsl_debug_console.h" #pragma language=extended void __init_stack_chk_guard(void); /* * This variable holds the value that the canary will be initialized with * and also compared with before returning from the function. */ #pragma required=__init_stack_chk_guard __no_init uint32_t __stack_chk_guard; /* * The __stack_chk_fail(void) function is called when a modified canary is detected. */ #pragma no_stack_protect __attribute__((noreturn)) void __stack_chk_fail(void) { /* LogStackSmashed(); */ //abort(); PRINTF("stack checkover find\r\n"); while(1); } /* * This function gets called at startup as part of construction of static C++ objects * and is a way to programatically initialize the __stack_chk_guard when the system starts. */ #pragma early_dynamic_initialization #pragma no_stack_protect __attribute__((constructor)) void __init_stack_chk_guard(void) { __stack_chk_guard = 4711; }
编译选项代码差异
对比有--stack_protection 编译选项和没有的同一函数的汇编代码,发现--stack_protection 时编译器,在函数的入口__stack_chk_guard 变量的值压入到堆栈中,函数退出时进行检查对应的栈空间的值是否被改变了如果被串改了则检出函数内部stack overflow 并调用__stack_chk_fail 上报对应的异常事件,从汇编的代码看出代码中会使用得到__stack_chk_guard,__stack_chk_fail 符号也解释了之前的link错误是如何产生的了,因为编译的函数代码中中会使用对应对应的符号,需用户来实现因此产生了link 错误。
--stack_protection 编译选项作用范围
从以上IAR 的 EWARM_DevelopmentGuide.ENU.pdf 的说明中提到该编译选项只适用于C/C++ 代码,其中的汇编代码该编译选项并不适用,从IAR 的汇编代码的编译命令也可以看出对应的代码编译的时候没有该编译选项。
比较最终编译出来的汇编文件中的接口对比发现,也是该编译选项对汇编的函数是没有任何影响的。
总结
虽然该编译选项可以在函数入口和出口产生堆栈溢出检查的代码,但是只是会检查一个word 4字节的空间,如果函数内部恰巧越过了这个空间发生了溢出时是无法检测出异常的,相对来说功能也很有限只能辅助检查不能做到100%抓到异常overflow 事件。并且对于汇编文件中的函数该编译选项也是不会生成对应的保护代码。