在main()之前,C / C ++程序可能会崩溃吗?
有没有什么办法可以使程序在main()之前崩溃?
使用gcc,你可以使用constructor属性来标记一个函数(这会导致函数在main
之前运行)。 在下面的函数中, premain
将在main
之前调用:
#include <stdio.h> void premain() __attribute__ ((constructor)); void premain() { fputs("premain\n", stdout); } int main() { fputs("main\n", stdout); return 0; }
所以,如果premain
有一个崩溃的bug,你会在main
之前崩溃。
是的,至less在Windows下。 如果程序使用DLL,则可以在main()
启动之前加载它们。 这些DLL的DllMain
函数将在main()
之前执行。 如果他们遇到错误,他们可能会导致整个过程中止或崩溃。
如果你有一个C ++程序,它可以在inputmain之前通过函数和构造函数初始化variables和对象。 任何这些错误都可能导致程序崩溃。
当然在c ++中; 带有构造器的静态对象将在main之前被调用 – 它们会死掉
不知道c
这里是示例
class X { public: X() { char *x = 0; *x = 1; } }; X x; int main() { return 0; }
这将在主要之前崩溃
简单的答案是: 是的 。
更具体地说,我们可以区分这两个原因。 我将把它们称为实现依赖和实现无关 。
一个完全不依赖于你的环境的情况就是C ++中的静态对象,在这里提到。 以下代码在main()
之前死亡:
#include <iostream> class Useless { public: Useless() { throw "You can't construct me!"; } }; static Useless object; int main() { std::cout << "This will never be printed" << std::endl; return 0; }
更有趣的是平台依赖的原因 。 有人在这里提到。 在这里提到的几次是使用dynamic链接库(windows中的DLL,Linux中的SO等) – 如果你的操作系统的loader在main()
之前加载它们,它们可能会导致你的应用程序在main()
。
这个原因的更一般的版本是在调用你的入口点( main()
)之前谈论你的二进制入口点所做的所有事情。 通常,当你build立你的二进制文件时,有一个非常严重的代码块,当你的操作系统的加载器开始运行你的二进制文件时,调用这个代码块,当它完成时调用你的main()
。 这个代码所做的一件常见事情是初始化C / C ++标准库。 这个代码可能会因为许多原因而失败(它试图为其分配的任何一种系统资源的短缺)。
在windows上的main()
之前执行代码的一个有趣的方法是使用TLScallback(google会告诉你更多关于它们的信息)。 这种技术通常被发现在恶意软件中,作为一个基本的反debugging技巧(这个技巧曾经愚弄ollydbg,不知道它是否仍然)。
问题是,你的问题实际上等价于“是否有加载二进制文件会导致用户代码在main()
?”中的代码之前执行的方法,而且答案是地狱!
依赖于在main之前加载的共享对象(DLL)的任何程序都可能在main之前失败。
在Linux下,dynamic链接库(ld – *。so)中的代码运行在main之前提供任何库依赖关系。 如果任何需要的库无法find,有不允许你访问它们的权限,不是正常的文件,或者没有一些符号链接你的程序的dynamic链接器认为它应该有什么时候它链接你的程序,那么这可能会导致失败。
另外,每个库链接时都会运行一些代码。 这主要是因为库可能需要链接更多的库或可能需要运行一些构造函数(甚至在C程序中,库可能有一些C ++或其他使用constroctor的其他东西)。 另外,标准C程序已经创build了stdio文件stdin,stdout和stderr。 在许多系统上,这些也可以closures。 这意味着它们也是自由的(),这意味着它们(和它们的缓冲区)是malloc(),可能会失败。 这也表明,他们可能已经做了一些其他的东西,文件描述符,这些文件结构代表,这可能会失败。
其他可能发生的事情可能是如果操作系统搞砸了传递给程序的环境variables和/或命令行参数。 在调用main之前,main之前的代码很可能已经与这个数据有关。
主要之前发生了很多事情。 他们中的任何一个都可以以一种致命的方式妥协地失败。
我不确定,但如果你有一个这样的全局variables:
static SomeClass object; int main(){ return 0; }
“SomeClass”构造函数可能在执行主体之前崩溃程序。
有很多可能性。
首先,我们需要了解执行main之前究竟发生了什么:
- 加载dynamic库
- 全局初始化
- 一个编译器,一些函数可以明确执行
现在,任何一个都可能会在几个方面导致崩溃:
- 通常未定义的行为(取消引用空指针,访问内存,你不应该…)
- 抛出的exception>因为没有
catch
,terminate
被调用,程序结束
这当然是很烦人的,而且可能很难debugging,这就是为什么你应该尽可能避免在main
之前执行代码,如果可以的话,更喜欢懒惰的初始化,或者在main
显式初始化。
当然,当它是一个DLL失败,你不能修改它,你是一个痛苦的世界。
C ++程序中的全局对象和静态对象将在main()中的第一个语句被执行之前调用它们的构造函数,所以构造函数中的一个错误会导致崩溃。
虽然这在C程序中不会发生。
sorting: http : //blog.ksplice.com/2010/03/libc-free-world/
如果你编译没有标准库,像这样:gcc -nostdlib -o hello hello.c
它不会知道如何运行main()并会崩溃。
这取决于“main之前”的含义,但是如果你的意思是“在main中的任何代码被实际执行之前”,那么我可以想一个例子:如果你在main中声明一个大数组作为局部variables,这个数组的大小超过了可用的堆栈空间,那么在执行第一行代码之前,你可能会在进入main时遇到stack overflow
。
一个有点人为的例子是:
int a = 1; int b = 0; int c = a / b; int main() { return 0; }
你不可能做这样的事情,但如果你做了很多macros观魔法,那完全有可能。
class Crash { public: Crash( int* p ) { *p = 0; } }; static Crash static_crash( 0 ); void main() { }
你还没有说过哪个平台/ libc。 在embedded式世界中,经常会有很多东西在main()
之前运行 – 很大程度上与平台设置有关 – 这可能会出错。 (或者,如果您在正规操作系统上使用时髦的链接器脚本,则所有的投注都closures,但我想这很less见。)
一些平台抽象库覆盖(我个人只知道像Qt或ACE这样的C ++库,这样做,但也许一些C库做这样的事情也是)“main”,以便他们指定一个特定于平台的主像一个int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );
并设置一些库的东西,将命令行参数转换为正常的int argc, char* argv[]
然后调用普通的int main(int argc, char* argv[])
当然这样的库可能会导致崩溃,当他们没有正确实现这个(也许是错误的命令行参数的原因)。
而对于不了解这一点的人来说,这可能看起来像main
之前的崩溃
我面临同样的问题。 find的根本原因是太多局部variables(巨大的数组)在主进程中被初始化,导致局部variables大小超过1.5MB。
由于堆栈指针非常大,操作系统检测到这个跳转是无效的,因此可能会导致恶意程序崩溃。
debugging这个。
1.启动GDB
2.在main上添加一个断点
3.拆卸主
4.检查sub $ 0xGGGGGGG,%esp
如果这个GGGGGG值太高,那么你会看到和我一样的问题。
因此,检查主要所有局部variables的总大小。
当然,如果在操作系统或运行时代码中存在错误。 C ++对于这种行为尤其臭名昭着,但它仍然可以在C中发生。