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