编译器stack操作

px4原生源码学习-(4)--Nuttx 实时操作系统编程 前面说到px4是基于Nuttx实时操作系统上的,那么px4也是由一些程序所构成,这些程序实现了飞行器的自主控制,只不过这些程序并不是我们通常所见到的单片机或者windows编程那样的程序,但基本编程思想是一致的。我认为如果要看懂px4的源码,那你一定要了解px4的那些程序是怎么编写出来,怎么运行的。所以本章我就大概介绍一下基于Nuttx的编程,我以一个所有编程入门都会介绍的一个程序作为例子。这个程 阅读详情
编译器stack操作

     标题起了这么一个奇怪的名字,呵呵,其实这个跟游戏制作没有什么大的关系,跟底层驱动程序到是有很大的关系.
     众所周知有个 crt ,他提供了很多有用的函数,比如 printf ,比如 strlen 等等,同时大家也知道 c++ 也依赖这个 crt ,而同时 c++ 里面的全局静态变量要在 main 函数运行之前完成初始话,相信也有人知道,其实我们自己写的 main 函数并不是在进程加载执行的时候操作系统调用的第一个用户态函数.等等的这些都是编译器链接器作的手脚.好在 ms 提供了全部的完整的 crt 的实现,花点时间就能了解到编译器究竟作了些什么额外的操作.
     这里一个额外的话题就是能不能让我们的程序不依赖编译器提供的 crt 呢?答案是肯定的,比如 kernel mode 的 driver 就不会也不能依赖编译提供的 crt ,而 driver studio 却是能用 c++ 语言的,那他是怎么作的呢?自然是自己提供了 crt 的支持,这个说起来就长了,要作的工作很多很多,有的简单(比如 new delete 的实现),有的复杂( c++ exception 的实现),不是这里讨论重点,如果有兴趣可以到 google 上面搜索,能找到不少的文章的.这里主要说的是 nt kernel 对 thread 的 stack 管理.
     一个内存区域在 win32 里面有两个操作,一个叫 reserve 一个叫 commit , 对一段内存进行 reserve 并不引发操作系统的内存分配动作,直到对 reserve 的内存区域进行 commit 操作的时候,操作系统才真正的进行内存描述符的分配,进行调页,详细的信息可以参考 inside windows 2000 和 advanced windows 这些书籍.在编译器里面,对于 stack 同样有这样的操作,/STACK 选项能设置线程的对 stack reserve 和  commit 的默认值,但是 上面说了必须要对 reserve 的内存进行一次 commit(调用一个 win32 api ) 以后,内存才被能被使用,但是我们并没有在自己的程序里面显式的进行 stack 的 commit 调用阿? 自然这也属于编译器的内部操作了.也许有人碰到过这种错误,编译器告诉说有个不能解析的外部符号叫 _chkstk (各个版本的 vc 有不同的名字,也许叫 _chkesp ).这个函数就是关键所在了.也是今天这个文章要讲解的主题.
    说了这么多的废话,不知道大家晕了没有.先作个实验,让你瞧瞧 _chkstk 所作的工作.首先,新建一个工程,选择 win32 console 的吧,最简单的只有一个 _tmain 函数的那种( vs.net 2003 ),然后选择项目->属性->链接器->输入->忽略所以默认库,选择是,然后链接器->高级->入口点,输入 main ,然后选择 c/c++ ->代码生成->基本运行时检查,选择默认,然后打开那个cpp文件,删除_tmain函数,如下输入
      int main()
     {
            volatile int a[10000];
           a[9999] = 0;

            return 0;
     }
     f7 ,出来错误了,error LNK2019: 无法解析的外部符号 __chkstk ,该符号在函数 "int __cdecl test(void)" (?test@@YAHXZ) 中被引用,说明在这个test的函数里面调用了这个 _chkstk 函数,接着再在文件里面加入
      extern "C" void _cdecl _chkstk()
     {
     }

     这次通过编译了,运行瞧瞧,出错了.为什么会这样呢?仔细检查下,没有语法错误阿,数组也没有越界啊,没有道理啊,我不说你也该知道了,问题出在那个 _chkstk 函数里面,既然编译器要求要使用到这个函数,那这个函数就绝对不是像我们自己提供的那个什么都不作的函数那样简单,好了,现在你也该明白 _chkstk函数的重要了,那究竟这个函数是什么样子的呢?是用来干什么的呢?先看他的源代码.新建一个工程,还是 win32 console ,这次什么都不要改,只是在 _tmain 函数里面加入上面 main 函数里面的内容,在 _tmain 函数的开头设置一个断点,转到反汇编,你就能看到类似下面的代码
     00411A13 mov eax,9D08h
     00411A18 call @ILT+250(__chkstk) (4110FFh)

     mov 了一个 eax ,然后调用 _chkstk 函数,跟踪进去,呃,是一个用汇编实现的函数.代码我就不贴上来了,再看看上面对这个描述的说明,你可能已经明白这个函数是干什么用的了,简单的说,他就是在 os 的配合下完成对 reserve 的 stack 在需要的时候进行 commit ,但是他也没有明确的调用进行内存 commit 的函数啊(说了半天都没有提到这个 win32 api 的名字,他叫 VirtualAlloc ),那又是怎么完成的呢? 上面说了,他是要在 os 的配合下面完成 stack 的操作的. 两个关键字 os 和 stack 都是不能少的.那你也许会问了, os 又是怎么完成这个操作的呢?看那个 _chkstk 函数只是循环每一个要访问的 page 作一次读操作( test [ecx],eax )而已啊,难不成这里面有隐藏什么东西?很不幸,被你言中了,关键就在那个 test 操作上面.
     上面说了,内存没有 commit 的时候,并没有建立适当的描述符的,这个时候对这个内存地址进行读写操作都会引发一个 page fault 异常, os 捕获这个异常,检查一定的条件,适合的时候就把这个内存页 commit 了.这就是原理.细心的你也许要问了,那上面两个程序有什么不同呢? os 不是在读取内存时候会进行自动的 commit 吗? 为什么第一个程序就会出错呢? 而第二个程序同样也是读内存啊,怎么就不出错呢?呵,上面也说了, os 会检查一定的条件,这个条件是什么呢? 首先, 能被自动 commit 的内存区域只能是 thread stack,其次, stack 必须连续的增长,不能跨越 page .第一个条件很容易想得通,第二个就不是那么的明显了.先看下来的图
     ____________
     |___________|   <-  当前 esp 所在的 page ,commit
     |___________|   <-  下一个 page ,commit 并且设置了guard 属性
     |___________|   <-  再下一个 page , reserve
     在当前使用的 page 的下一个 page,设置了guard 的属性,访问这个 page 的时候会发生 page fault ,os 检查 page 的 guard 属性,发现是有这个属性的,接着 把第3个 page commit 了,并且设置 guard 属性,这样循环下去就完成了 stack 的自动 commit.如果一个 page 没有 guard 属性,而只是reserve 的话,这个时候 os 就不会进行自动的 commit 了,而是引起一个访问非法的异常.就像我们上面的那个程序一样.编译器在编译的是时候,默认给 thread reserve 1M 的 stack ,并且 commit 2 个 page ,第二个 page 有 guard 属性,而我们的那个程序使用的内存却不是第二个 page ,而是越过了它,访问了后面的 page ,而后面的 page 却是 reserve 的,所以就引发一个非法访问的异常.而后面的那个程序,因为使用了一个循环一一访问每一个 page ,这样在 os 的帮助下就完成了一一 commit page 的任务,明白了么? 事实胜于雄辩,我们来看看是不是这么一回事情.
     这里要还另外的一个程序了,soft-ice,操作我不多说,symbol load加载刚刚编译出来的后一个程序,自动断点到入口,f3切换到源代码和汇编代码混合模式,f8单步进入 _chkstk 函数内部,断点到 test [ecx],eax这一行.page ecx 看看,呃 这个页是 present 的,g ,断点 再 page ecx,不出意外这个 page 应该是 no present 的,这个时候 bpint 0e ,在 x86 平台上面 0e 就是 page fault 中断,然后在 test 语句下面下断点,记录下这个 ecx 的值,接着 g,嗯,断点了,在 0e 中断里面,看看 cr2 的值是不是和刚刚记录的 ecx 的值一样(因为 ss 段的 base 是0)?这个时候不能直接 g 了,bd 那个 int 0e 断点,然后再 g ,回到了test 下面的语句.这些行为都证实了我们上面的说法.唯一没有提到的就是 os 对 0e 中断的处理方式了.可惜小 T 并不是一个逆向工程的高手,不然大可以把 0e 中断处理函数的代码弄出来给大家详细的讲解.不过这些都因为 win 2000 的源代码泄漏而迎刃而解了,在源代码包的 private/ntos/ke/i386 目录下面有个 trap.asm 文件,里面定义 _KiTrap0E 函数就是用来处理 0e 中断的,(有点怕怕,讲这个东西会不会被 fbi 抓,-_-#),这个函数首先保留现场,然后作些检查,push 适当的参数,转调MmAccessFault函数,这个函数在 private/ntos/mm/mmfault.c 里面定义,还算 well documented.哈. 这个函数非常的长,小 T 也没有完全的弄明白全部的 if else 的分支走向,大致的看看,前面的部分是处理 kernel mode 的 page fault,后面从 UserFault 标号开始的才的 user mode 的 page fault,valid 各个 pde pte 以后,来到 1366 行,这里是处理 MM_GUARD_PAGE 的地方,也就是我们这次的关键了.首先清除这个 pte 的 GUARD 标记,然后进入 MiCheckForUserStackOverflow 函数,这个函数在同一个目录下面的 acceschk.c 文件里面,上面有个图跟小 T 画的差不多.这个函数要作很多必须的检查,小 T 也没有详细的分析,只是看了看我们这次的关键部分,重新分配了一个 GUARD 属性的 page (上一个 page 的 GUARD 已经被清除了),同时如果 stack 到了 reverse 的最低一个 page ,这个page 并不被 commit .最后这个函数返回 STATUS_PAGE_FAULT_GUARD_PAGE 值,然后MmAccessFault函数也返回到_KiTrap0E,这个时候 eax = STATUS_PAGE_FAULT_GUARD_PAGE,or 结果非负,跳转到 Kt0e10 ,接着恢复现场,完成 int 0e 的处理.这就是对于 stack 这种能自动 commit 的 page 的操作,对于访问的不是 guard 的 page ,引发一个非法访问异常,也许被高层的 __try 捕获,也许就结束了整个进程.这个部分,小 T 也说得语焉不详,因为小 T 也没有详细的分析过2000的源代码,只是凭借源代码里面的的注释,已经那些符号推测的执行流程.不过大体上应该不会有什么差错,具体的信息能在 inside windows 2000 以及 advanced windows 里面找到.小 T 上面写的只是在自己走马观花的情况下得出来的流程,不一定正确.有兴趣的朋友可以多多研究,虽然 win2000 的源代码泄漏出来的部分,据ms说只有那么不到 1% ,但是,很不幸,没有泄漏出来的东西在小 T 看来都不是那么重要,最最关键的进程线程管理,内存管理,i/o 调度大都展现在各位的眼前,ms 声称有 40个 G 的完整源代码最最精华的关键就在这 40 M上面了.哈,ms 有的哭了.
     也许大家会奇怪,我为什么要写这么一个文章,因为偶现在正研究设备驱动程序,正努力的搞清除编译器内部的动作,哈.

【自定义Xcode编译器】Custom Compiler in Xcode 如果你想要在Xcode下做嵌入式开发进行交叉编译, 那么应该在Xcode中添加对应系统平台的编译器。 如何来实现?       1.首先应当看一下这篇文章:Add a custom compiler to XCode 3.2(任何版本都可以) - Stack Overflow (google一下) Xcode版本无关紧要。 按照这篇文章操作完后,如果成功,那就恭喜你不需要再进行下一 阅读详情

相关推荐

深度探索:非线性随机森林(Nonlinear Random Forest,NL-RF)原理及应用

非线性随机森林(NL-RF)旨在通过结合集成方法与高级技术来捕捉和建模数据中的非线性关系,从而改善传统随机森林在处理复杂非线性数据结构时的表现。

qq_51320133的博客 2067

edk2最新版本2024.11.01编译问题

FilterLibNull.lib(RegisterFilterLibNull.obj) : error LNK2001: 无法解析的外部符号 __security_check_cookie。BaseLib.lib(SafeString.obj) : error LNK2001: 无法解析的外部符号 __security_check_cookie。BaseLib.lib(SafeString.obj) : error LNK2001: 无法解析的外部符号 __security_cookie。

ahcybh的博客 729

Ubuntu断网环境下的系统更新:apt-offline保姆级教程(附常见问题排查)

本文详细介绍了在Ubuntu断网环境下使用apt-offline进行系统更新的完整教程,包括环境准备、签名文件生成、数据传输方案对比及常见问题排查。特别适用于企业级开发环境或安全敏感场景,帮助运维人员高效完成离线系统更新,确保系统安全与稳定。

weixin_29214691的博客 359

vc 编译选项

tbw

princess9的专栏 381

opencv 常用函数总结

本文是一份OpenCV图像处理的常用函数总结,详细介绍了图像的读入、显示、保存、像素处理、图像属性获取、基础操作、阈值处理、图像平滑、形态学转换以及直方图处理等关键技术。文章通过代码示例和参数说明,帮助读者快速掌握OpenCV在图像处理领域的应用。

weixin_44063529的博客 1599

竞赛无人机模块化开发实战:2023 TI电赛G题空地协同消防系统(飞控与视觉融合篇)

本文以2023年TI电赛G题为例,详细阐述了采用模块化开发思维构建空地协同智能消防系统的实战方法。文章重点拆解了无人机飞控二次开发、视觉识别与融合控制等核心模块,提供了硬件选型、代码实现与系统联调的完整指南,旨在帮助参赛者通过“搭积木”式编程高效完成复杂系统集成。

js777的博客 454

VS编译的程序出现内存不够的解决办法

LINK的/STACK选项    /STACK   :reserve[,commit]    reserve:栈总大小    commit:程序开始时系统提供的实际内存量    缺省:1M,8K    参数为0取缺省值      今天在VC++.NET中运行聚类程序,老是说Stack   OverFlow,  后来才发现是栈空间太小了。 单单保

heyanhema的专栏 2万+

Android手机本地语音转文字实战:用FunASR+ONNX Runtime打造离线ASR应用(附模型下载)

本文详细介绍了如何在Android应用中集成FunASR和ONNX Runtime,构建高性能的离线语音识别系统。通过模型选择、工程化集成和性能调优等实战技巧,开发者可以实现本地语音转文字功能,有效保护用户隐私并提升响应速度。

weixin_29046611的博客 266

编译链接过程中出现“无法解析的外部符号”,原因及解决办法总结

1、错误为:error LNK2019: 无法解析的外部符号 ___report_rangecheckfailure,该符号在函数 _OBJ_create_objects 中被引用 原因:__report_rangecheckfailure是用来检查堆栈缓存溢出的,如果编译的时候打开GS(project property--》Configuration properties--》c/c++......

fanzirong_的博客 12万+

使用Python爬虫抓取政府网站政务信息:公告、通知的自动化抓取与分析

通过结合Requests、BeautifulSoup、Selenium等技术,你能够高效地抓取政府网站的数据,并通过MySQL存储与Pandas分析这些数据。通过自动化手段抓取这些信息,不仅可以提高数据收集的效率,还能够实时获取最新的政务动态,为政策研究、舆情分析等提供宝贵数据。我们将通过Python的Requests、BeautifulSoup、Selenium等工具,结合最新的技术实现一个完整的爬虫系统,帮助你轻松抓取并分析政务信息。通过爬虫,我们可以自动化地从指定网站上抓取公告、新闻、通知等信息。

2201_76125261的博客 1907

基于YOLOv11的人脸识别系统:从构建到应用的全流程指南

深度学习是机器学习的一个分支,旨在通过多层神经网络模拟人脑的认知能力。它能够从大量数据中自动提取特征,并进行准确的预测和分类。在计算机视觉领域,深度学习技术已经取得了显著的成果,尤其是在图像识别、目标检测等方面。YOLOv11是YOLO系列模型的最新版本,结合了卷积神经网络(CNN)和其他创新技术。它在保持高精度的同时,显著提高了推理速度。YOLOv11的结构轻量且高效,非常适合实时目标检测任务。人脸检测的目标是从图像或视频中定位所有人脸的位置。

FJN110的博客 184

linux内核中的宏定义之noinline_for_stack

内联(inline)是一种编译器优化技术,它通过将函数的代码直接插入到每个调用点来减少函数调用的开销,从而提高程序的执行效率。但是,在某些情况下,我们可能不希望函数被内联,比如当函数体较大或者为了调试方便时。这个宏的定义可以在内核代码中看到,它提供了一种简洁的方式来应用noinline属性,是一个宏定义,它用于告诉编译器不要将标记的函数内联,即使它们被声明为。是一个函数属性,它的作用是防止编译器将函数进行内联优化。属性可以阻止编译器进行内联。这个宏的定义很简单,就是。在Linux内核代码中,

ATFWUS的博客 461

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

gcc undefined reference to '__stack_chk_fail'【操作系统开发编译中遇到的问题】

<br /> <br />今天看一本os的书,照着写,然后在ubuntu下用gcc编译链接,出现错误<br />undefined reference to `__stack_chk_fail’<br /><br /><br />上网找了一下,解决方法是:<br />add -fno-stack-protector to your CFLAGS.<br /><br /><br />原因是:GCC likes to do a bunch of stuff behind the scenes. It’s gre

Micphone的专栏 2383

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

数据结构-栈(Stack)

一、什么是栈 栈是一种线性结构,相对于数组,栈对应的操作是数组的子集,只能从一端添加元素,也只能从一端取出元素 ,这一端成为栈顶。栈是一种后进先出的数据结构(Last In First Out–LIFO),在计算机的世界里,栈拥有着不可思议的作用。 二、栈的应用 2.1 在word或者其他编辑器中,当我们输入一些文本内容的时候,就用到了栈。(举例:”沉迷学习无法自拔”) 在这个栈中,沉迷,学习,...

我是太阳 1万+

stack smashing detect错误修正

运行./a.out程序时候出现如下: *** stack smashing detected ***: ./a.out terminated段错误 (核心已转储) 一般这个错误是由于堆栈错误,很可能是编译器的不同造成的。所以可以先在编译的时候加入 -fno-stack-protector 编译时候如下gcc my_replace.c -fno-stack-protector。...

weixin_30680385的博客 525

使用g++编译器扩大程序可用栈空间

如题,在写一些程序的时候我们有时会开一个比较大的数组或进行层数较多的dfs。这时候,程序常常会报错,于是就很无奈。 其实,虽然Windows给程序的默认栈空间比较小,我们还是有办法去扩大这个程序运行栈空间的。 栈空间限制在何处? 对于 Linux,在系统设置(本文不做讨论,后续可能会补坑) 对于 Windows,在 ".exe" 可执行文件中。 Windows + g++ 扩大程序可用栈空间 方法...

weixin_30746117的博客 1958

linux下编译C++程序出现错误 “*** stack smashing detected ***, eclipse解决方法

如果运行的时候报“*** stack smashing detected ***, 那是因为你的系统默认使用了GCC的“ -fstack-protector"参数导致的,我们只需要在编译的时候 export CFLAGS="-fno-stack-protector"就好。 eclipse里面配置位置:C/C++ Build -> Setting -> GCC C++ Compiler ->

newSky的专栏 1296

struct函数测试(gcc的栈保护机制)

struct函数测试 源代码: #include <stdio.h> #include <stdlib.h> typedef struct { int a[2]; double d; } struct_t; double fun(int i) { volatile struct_t s; s.d = 3.14; s.a[i] = ...

qq_44813266的博客 285

stack unwinding & on_scope_exit & c++ exception:构造 析构 异常处理

在构造函数中可以抛出异常,但是该对象的析构函数不会被调用(因为异常导致该对象没有被正确生成,也就没有必要对其析构),所以,某些需要被清理的工作应该考虑 on_scope_exit 处理,例如将分配到内存放入智能指针中,这样即使析构函数不被调用也不会出现内存泄露。void f() { X x; // If X::X() throws, the memory for x it

all for one,one for all 3137

java知识点、集合类、Stack栈、Queue队列

Stack栈 1.先进后出 2.浏览器的后退、编译器的撤销、安卓Activity的返回都属于栈的功能 3.虽然Stack是Vector的子类,但使用时使用的不是Vector的方法,不要向上转型。因为操作的方法不是由List定义的,而是由Stack定义的。 4.常用方法 public E push(E item){} public synchronized E pop(){} public sy...

heihei233的博客 178

153、【OS】【Nuttx】【栈溢出】回归!继续 Nuttx 探索(Stack Monitor)

本文介绍了Nuttx系统中的栈溢出检测机制。首先通过CONFIG_STACK_COLORATION配置开启栈监视器功能,使用固定值填充栈空间以检测溢出。其次,详细分析了Stack Monitor守护进程的工作原理及其局限性,指出它无法检测跳跃式栈溢出。最后,提出了Per function Call机制,通过编译器插桩和保留R10寄存器来实时检查栈指针越界。文章还提到新建的Gitee仓库用于存放Nuttx源备份,并预告了后续内容。

HIT_Weston的博客 871

栈(Stack)

栈(Stack) 栈(stack)又名堆栈,它是一种运算受限的线性表。 限定仅在表尾进行插入和删除操作的线性表。 这一端被称为栈顶,相对地,把另一端称为栈底。 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。 栈的API: 入栈(push):在栈顶添加元素,O...

隐身的六娃 285

如何快速上手Haste Compiler?从安装到编译的完整教程

Haste Compiler是一款基于GHC的Haskell转JavaScript编译器,它能帮助开发者将Haskell代码高效编译为可在浏览器中运行的JavaScript。本教程将为你提供从安装到编译的完整指南,让你快速掌握这一强大工具的使用方法。 ## 一、安装Haste Compiler的3种简单方法 ### 1.1 通过Hackage安装(推荐新手) Hackage是Haskell的包

gitblog_00829的博客 346

STL-stack容器和queue容器

STL-stack容器和queue容器

好好学习,天天向上 523

C++ 栈帧探测机制:利用 C++ 结合编译器防溢出技术(Stack Probing)识别大规模局部变量导致的栈破坏

栈帧探测机制是现代编译器和操作系统为 C++ 程序提供的一道重要防线,它能在大规模局部变量导致栈溢出,破坏关键数据之前,及时发现并阻止程序继续运行。理解栈帧的构成、栈溢出的原理以及栈探测的工作方式,对于编写健壮、可靠的 C++ 代码至关重要。结合编译器的防溢出技术、强大的调试工具和良好的编程习惯,我们可以有效地避免栈破坏带来的隐患,确保程序的稳定性和安全性。

weixin_41455464的博客 42
上一篇: c++对象内存布局(转)
下一篇: C++字符串完全指南 - Win32字符编码(二)
evers
博客等级 码龄24年 7粉丝 4原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值