[英]Is there any way a C/C++ program can crash before main()?
程序在main()之前有什么方法可以崩溃吗?
使用gcc,您可以使用构造函数属性标记函数(这会导致函数在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
之前崩溃。
是的,至少在Windows下。 如果程序使用DLL,则可以在main()
启动之前加载它们。 这些DLL的DllMain
函数将在main()
之前执行。 如果他们遇到错误,他们可能会导致整个过程停止或崩溃。
如果你有一个C ++程序,它可以在输入main之前通过函数和构造函数初始化变量和对象。 任何这些中的错误都可能导致程序崩溃。
肯定是在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;
}
更有趣的是与平台相关的原因 。 这里提到了一些。 这里提到的一个是动态链接库(Windows中的DLL,Linux中的SO等)的使用 - 如果你的操作系统的加载器在main()
之前加载它们,它们可能会导致你的应用程序在main()
之前死掉main()
。
这项事业的更一般的版本调用您的入口点之前谈论所有的二进制文件的入口点做的事情( main()
通常在构建二进制文件时,会有一个非常严重的代码块,当操作系统的加载器开始运行二进制文件时,它会被调用,完成后会调用main()
。 这段代码的一个常见问题是初始化C / C ++标准库。 此代码可能由于多种原因而失败(它尝试为一个系统资源分配的任何类型的系统资源不足)。
在Windows上的main()
之前,二进制文件执行代码的一个有趣的方法是使用TLS回调(谷歌会告诉你更多关于它们的信息)。 这种技术通常在恶意软件中被发现作为一种基本的反调试技巧(这个技巧当时用来欺骗ollydbg,不知道它是否仍然存在)。
关键是你的问题实际上等同于“有没有办法加载二进制文件会导致用户代码在main()
的代码之前执行?”,答案是地狱,是的!
依赖于在main之前加载的共享对象(DLL)的任何程序都可能在main之前失败。
在Linux下,动态链接器库(ld - * .so)中的代码运行以在main之前提供任何库依赖项。 如果无法找到任何所需的库,具有不允许您访问它们的权限,不是普通文件,或者没有一些符号表示链接您的程序的动态链接器认为它应该具有它链接了你的程序然后这可能会导致失败。
此外,每个库在链接时都会运行一些代码。 这主要是因为库可能需要链接更多的库或者可能需要运行一些构造函数(即使在C程序中,库也可能有一些C ++或其他使用constroctors的东西)。 此外,标准C程序已经创建了stdio FILEs stdin,stdout和stderr。 在许多系统上,这些也可以关闭。 这意味着它们也是free()ed,这意味着它们(和它们的缓冲区)是malloc()ed,它们可能会失败。 它还表明他们可能已经对那些FILE结构所代表的文件描述符做了一些其他的事情,这可能会失败。
其他可能发生的事情可能是操作系统要设置环境变量和/或传递给程序的命令行参数。 在调用main之前,main之前的代码可能不得不使用这些数据。
主要之前发生了很多事情。 他们中的任何一个都可以以致命的方式失败。
我不确定,但是如果你有一个像这样的全局变量:
static SomeClass object;
int main(){
return 0;
}
'SomeClass'构造函数可能会在main执行之前使程序崩溃。
有很多种可能性。
首先,我们需要了解main执行之前实际发生了什么:
现在,任何这种情况都可能在几个方面导致崩溃:
catch
,所以调用terminate
并且程序结束 它当然真烦人,可能很难调试,这就是为什么你应该尽可能避免在main
之前执行代码,如果可以的话,或者在main
显式初始化,更喜欢延迟初始化。
当然,当它是一个DLL失败并且你无法修改它时,你就陷入了痛苦的世界。
排序: http : //blog.ksplice.com/2010/03/libc-free-world/
如果你在没有标准库的情况下编译,就像这样:gcc -nostdlib -o hello hello.c
它不会知道如何运行main()并将崩溃。
C ++程序中的全局和静态对象将在执行main()中的第一个语句之前调用其构造函数,因此其中一个构造函数中的错误可能导致崩溃。
但是,在C程序中不会发生这种情况。
这取决于你在“在main之前”的意思,但是如果你的意思是“在你的main中的任何代码实际执行之前”那么我可以想到一个例子:如果你在main中声明一个大数组作为局部变量,这个数组的大小超过了可用的堆栈空间,那么在第一行代码执行之前,你可能会在进入main时遇到stack overflow
。
一个有点人为的例子是:
int a = 1;
int b = 0;
int c = a / b;
int main()
{
return 0;
}
你不可能做过这样的事情,但是如果你做了很多宏观魔术,这是完全可能的。
class Crash
{
public:
Crash( int* p )
{ *p = 0; }
};
static Crash static_crash( 0 );
void main()
{
}
我遇到过同样的问题。 发现的根本原因是......在主进程中初始化了太多局部变量(大型数组),导致局部变量大小超过1.5 mb。
这导致一个大的跳跃,因为堆栈指针非常大,并且操作系统将此跳转检测为无效并且因为它可能是恶意的而导致程序崩溃。
调试这个。
1.启动GDB
2.在main处添加断点
3.拆卸主体
4.检查子$ 0xGGGGGGG,%esp
如果此GGGGGG值过高,您将看到与我相同的问题。
因此,检查main中所有局部变量的总大小。
当然,如果操作系统或运行时代码中存在错误。 C ++对于这种行为特别臭名昭着,但它仍然可以在C中发生。
你还没有说过哪个平台/ libc。 在嵌入式世界中,经常有很多东西在main()
之前运行 - 主要与平台设置有关 - 这可能会出错。 (或者,如果你在常规操作系统上使用时髦的链接器脚本,所有的赌注都会关闭,但我想这很少见。)
一些平台抽象库覆盖(我个人只知道C ++库,如Qt或ACE,这样做,但也许一些C库做类似的东西)“main”,所以他们指定一个特定于平台的主要像int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );
并设置一些库的东西,将命令行args转换为普通的int argc, char* argv[]
然后调用普通的int main(int argc, char* argv[])
当然,这些库可能会导致崩溃,因为它们没有正确实现这一点(可能导致命令行参数格式错误)。
对于那些不了解这一点的人来说,这可能看起来像是在main
之前的崩溃
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.